1 <?php rcs_id('$Id: BlockParser.php,v 1.42 2004-03-24 19:39:02 rurban Exp $');
2 /* Copyright (C) 2002, Geoffrey T. Dairiki <dairiki@dairiki.org>
4 * This file is part of PhpWiki.
6 * PhpWiki is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * PhpWiki is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with PhpWiki; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 require_once('lib/HtmlElement.php');
21 require_once('lib/CachedMarkup.php');
22 require_once('lib/InlineParser.php');
24 ////////////////////////////////////////////////////////////////
29 * Deal with paragraphs and proper, recursive block indents
30 * for the new style markup (version 2)
32 * Everything which goes over more than line:
33 * automatic lists, UL, OL, DL, table, blockquote, verbatim,
39 * FIXME: unify this with the RegexpSet in InlineParser.
42 * @author: Geoffrey T. Dairiki
46 * Return type from RegexpSet::match and RegexpSet::nextMatch.
50 class AnchoredRegexpSet_match {
57 * The text following the matched text.
62 * Index of the regular expression which matched.
68 * A set of regular expressions.
70 * This class is probably only useful for InlineTransformer.
72 class AnchoredRegexpSet
76 * @param $regexps array A list of regular expressions. The
77 * regular expressions should not include any sub-pattern groups
78 * "(...)". (Anonymous groups, like "(?:...)", as well as
79 * look-ahead and look-behind assertions are fine.)
81 function AnchoredRegexpSet ($regexps) {
82 $this->_regexps = $regexps;
83 $this->_re = "/((" . join(")|(", $regexps) . "))/Ax";
87 * Search text for the next matching regexp from the Regexp Set.
89 * @param $text string The text to search.
91 * @return object A RegexpSet_match object, or false if no match.
93 function match ($text) {
94 if (! preg_match($this->_re, $text, $m)) {
98 $match = new AnchoredRegexpSet_match;
99 $match->postmatch = substr($text, strlen($m[0]));
100 $match->match = $m[1];
101 $match->regexp_ind = count($m) - 3;
106 * Search for next matching regexp.
108 * Here, 'next' has two meanings:
110 * Match the next regexp(s) in the set, at the same position as the last match.
112 * If that fails, match the whole RegexpSet, starting after the position of the
115 * @param $text string Text to search.
117 * @param $prevMatch A RegexpSet_match object
119 * $prevMatch should be a match object obtained by a previous
120 * match upon the same value of $text.
122 * @return object A RegexpSet_match object, or false if no match.
124 function nextMatch ($text, $prevMatch) {
125 // Try to find match at same position.
126 $regexps = array_slice($this->_regexps, $prevMatch->regexp_ind + 1);
131 $pat= "/ ( (" . join(')|(', $regexps) . ") ) /Axs";
133 if (! preg_match($pat, $text, $m)) {
137 $match = new AnchoredRegexpSet_match;
138 $match->postmatch = substr($text, strlen($m[0]));
139 $match->match = $m[1];
140 $match->regexp_ind = count($m) - 3 + $prevMatch->regexp_ind + 1;;
147 class BlockParser_Input {
149 function BlockParser_Input ($text) {
151 // Expand leading tabs.
152 // FIXME: do this better.
154 // We want to ensure the only characters matching \s are ' ' and "\n".
156 $text = preg_replace('/(?![ \n])\s/', ' ', $text);
157 assert(!preg_match('/(?![ \n])\s/', $text));
159 $this->_lines = preg_split('/[^\S\n]*\n/', $text);
162 // Strip leading blank lines.
163 while ($this->_lines and ! $this->_lines[0])
164 array_shift($this->_lines);
165 $this->_atSpace = false;
168 function skipSpace () {
169 $nlines = count($this->_lines);
171 if ($this->_pos >= $nlines) {
172 $this->_atSpace = false;
175 if ($this->_lines[$this->_pos] != '')
178 $this->_atSpace = true;
180 return $this->_atSpace;
183 function currentLine () {
184 if ($this->_pos >= count($this->_lines)) {
187 return $this->_lines[$this->_pos];
190 function nextLine () {
191 $this->_atSpace = $this->_lines[$this->_pos++] === '';
192 if ($this->_pos >= count($this->_lines)) {
195 return $this->_lines[$this->_pos];
198 function advance () {
199 $this->_atSpace = $this->_lines[$this->_pos++] === '';
203 return array($this->_pos, $this->_atSpace);
206 function setPos ($pos) {
207 list($this->_pos, $this->_atSpace) = $pos;
210 function getPrefix () {
214 function getDepth () {
219 if ($this->_pos < count($this->_lines))
220 return $this->_lines[$this->_pos];
225 function _debug ($tab, $msg) {
227 $where = $this->where();
228 $tab = str_repeat('____', $this->getDepth() ) . $tab;
229 printXML(HTML::div("$tab $msg: at: '",
235 class BlockParser_InputSubBlock extends BlockParser_Input
237 function BlockParser_InputSubBlock (&$input, $prefix_re, $initial_prefix = false) {
238 $this->_input = &$input;
239 $this->_prefix_pat = "/$prefix_re|\\s*\$/Ax";
240 $this->_atSpace = false;
242 if (($line = $input->currentLine()) === false)
243 $this->_line = false;
244 elseif ($initial_prefix) {
245 assert(substr($line, 0, strlen($initial_prefix)) == $initial_prefix);
246 $this->_line = (string) substr($line, strlen($initial_prefix));
247 $this->_atBlank = ! ltrim($line);
249 elseif (preg_match($this->_prefix_pat, $line, $m)) {
250 $this->_line = (string) substr($line, strlen($m[0]));
251 $this->_atBlank = ! ltrim($line);
254 $this->_line = false;
257 function skipSpace () {
258 // In contrast to the case for top-level blocks,
259 // for sub-blocks, there never appears to be any trailing space.
260 // (The last block in the sub-block should always be of class tight-bottom.)
261 while ($this->_line === '')
264 if ($this->_line === false)
265 return $this->_atSpace == 'strong_space';
267 return $this->_atSpace;
270 function currentLine () {
274 function nextLine () {
275 if ($this->_line === '')
276 $this->_atSpace = $this->_atBlank ? 'weak_space' : 'strong_space';
278 $this->_atSpace = false;
280 $line = $this->_input->nextLine();
281 if ($line !== false && preg_match($this->_prefix_pat, $line, $m)) {
282 $this->_line = (string) substr($line, strlen($m[0]));
283 $this->_atBlank = ! ltrim($line);
286 $this->_line = false;
291 function advance () {
296 return array($this->_line, $this->_atSpace, $this->_input->getPos());
299 function setPos ($pos) {
300 $this->_line = $pos[0];
301 $this->_atSpace = $pos[1];
302 $this->_input->setPos($pos[2]);
305 function getPrefix () {
306 assert ($this->_line !== false);
307 $line = $this->_input->currentLine();
308 assert ($line !== false && strlen($line) >= strlen($this->_line));
309 return substr($line, 0, strlen($line) - strlen($this->_line));
312 function getDepth () {
313 return $this->_input->getDepth() + 1;
317 return $this->_input->where();
322 class Block_HtmlElement extends HtmlElement
324 function Block_HtmlElement($tag /*, ... */) {
325 $this->_init(func_get_args());
329 function setTightness($top, $bottom) {
330 $this->setInClass('tightenable');
331 $this->setInClass('top', $top);
332 $this->setInClass('bottom', $bottom);
336 class ParsedBlock extends Block_HtmlElement {
338 function ParsedBlock (&$input, $tag = 'div', $attr = false) {
339 $this->Block_HtmlElement($tag, $attr);
340 $this->_initBlockTypes();
341 $this->_parse($input);
344 function _parse (&$input) {
345 for ($block = $this->_getBlock($input); $block; $block = $nextBlock) {
346 while ($nextBlock = $this->_getBlock($input)) {
347 // Attempt to merge current with following block.
348 if (! ($merged = $block->merge($nextBlock)) ) {
349 break; // can't merge
353 $this->pushContent($block->finish());
358 function _initBlockTypes () {
359 foreach (array('oldlists', 'list', 'dl', 'table_dl',
360 'blockquote', 'heading', 'hr', 'pre', 'email_blockquote',
363 $class = "Block_$type";
365 $this->_block_types[] = $proto;
366 $this->_regexps[] = $proto->_re;
368 $this->_regexpset = new AnchoredRegexpSet($this->_regexps);
371 function _getBlock (&$input) {
372 $this->_atSpace = $input->skipSpace();
374 if (($line = $input->currentLine()) === '')
377 $tight_top = !$this->_atSpace;
378 $re_set = &$this->_regexpset;
379 for ($m = $re_set->match($line); $m; $m = $re_set->nextMatch($line, $m)) {
380 $block = $this->_block_types[$m->regexp_ind];
381 //$input->_debug('>', get_class($block));
383 if ($block->_match($input, $m)) {
384 //$input->_debug('<', get_class($block));
385 $tight_bottom = ! $input->skipSpace();
386 $block->_setTightness($tight_top, $tight_bottom);
389 //$input->_debug('[', "_match failed");
394 trigger_error("Couldn't match block: '$line'", E_USER_NOTICE);
399 class WikiText extends ParsedBlock {
400 function WikiText ($text) {
401 $input = new BlockParser_Input($text);
402 $this->ParsedBlock($input);
406 class SubBlock extends ParsedBlock {
407 function SubBlock (&$input, $indent_re, $initial_indent = false,
408 $tag = 'div', $attr = false) {
409 $subinput = new BlockParser_InputSubBlock($input, $indent_re, $initial_indent);
410 $this->ParsedBlock($subinput, $tag, $attr);
415 * TightSubBlock is for use in parsing lists item bodies.
417 * If the sub-block consists of a single paragraph, it omits
418 * the paragraph element.
420 * We go to this trouble so that "tight" lists look somewhat reasonable
421 * in older (non-CSS) browsers. (If you don't do this, then, without
422 * CSS, you only get "loose" lists.
424 class TightSubBlock extends SubBlock {
425 function TightSubBlock (&$input, $indent_re, $initial_indent = false,
426 $tag = 'div', $attr = false) {
427 $this->SubBlock($input, $indent_re, $initial_indent, $tag, $attr);
429 // If content is a single paragraph, eliminate the paragraph...
430 if (count($this->_content) == 1) {
431 $elem = $this->_content[0];
432 if (isa($elem, 'XmlElement') and $elem->getTag() == 'p') {
433 assert($elem->getAttr('class') == 'tightenable top bottom');
434 $this->setContent($elem->getContent());
443 function _match (&$input, $match) {
444 trigger_error('pure virtual', E_USER_ERROR);
447 function _setTightness ($top, $bot) {
448 $this->_element->setTightness($top, $bot);
451 function merge ($followingBlock) {
456 return $this->_element;
460 class Block_blockquote extends BlockMarkup
464 var $_re = '\ +(?=\S)';
466 function _match (&$input, $m) {
467 $this->_depth = strlen($m->match);
468 $indent = sprintf("\\ {%d}", $this->_depth);
469 $this->_element = new SubBlock($input, $indent, $m->match,
474 function merge ($nextBlock) {
475 if (get_class($nextBlock) == get_class($this)) {
476 assert ($nextBlock->_depth < $this->_depth);
477 $nextBlock->_element->unshiftContent($this->_element);
478 $nextBlock->_tight_top = $this->_tight_top;
485 class Block_list extends BlockMarkup
487 //var $_tag = 'ol' or 'ul';
493 | [*] (?!(?=\S)[^*]*(?<=\S)[*](?:\\s|[-)}>"\'\\/:.,;!?_*=]) )
496 var $_content = array();
498 function _match (&$input, $m) {
499 // A list as the first content in a list is not allowed.
502 // Should markup as <ul><li>* Item</li></ul>,
503 // not <ul><li><ul><li>Item</li></ul>/li></ul>.
505 if (preg_match('/[*#+-o]/', $input->getPrefix())) {
510 $indent = sprintf("\\ {%d}", strlen($prefix));
512 $bullet = trim($m->match);
513 $this->_tag = $bullet == '#' ? 'ol' : 'ul';
514 $this->_content[] = new TightSubBlock($input, $indent, $m->match, 'li');
518 function _setTightness($top, $bot) {
519 $li = &$this->_content[0];
520 $li->setTightness($top, $bot);
523 function merge ($nextBlock) {
524 if (isa($nextBlock, 'Block_list') && $this->_tag == $nextBlock->_tag) {
525 array_splice($this->_content, count($this->_content), 0,
526 $nextBlock->_content);
533 return new Block_HtmlElement($this->_tag, false, $this->_content);
537 class Block_dl extends Block_list
541 function Block_dl () {
542 $this->_re = '\ {0,4}\S.*(?<!'.ESCAPE_CHAR.'):\s*$';
545 function _match (&$input, $m) {
546 if (!($p = $this->_do_match($input, $m)))
548 list ($term, $defn, $loose) = $p;
550 $this->_content[] = new Block_HtmlElement('dt', false, $term);
551 $this->_content[] = $defn;
552 $this->_tight_defn = !$loose;
556 function _setTightness($top, $bot) {
557 $dt = &$this->_content[0];
558 $dd = &$this->_content[1];
560 $dt->setTightness($top, $this->_tight_defn);
561 $dd->setTightness($this->_tight_defn, $bot);
564 function _do_match (&$input, $m) {
565 $pos = $input->getPos();
567 $firstIndent = strspn($m->match, ' ');
568 $pat = sprintf('/\ {%d,%d}(?=\s*\S)/A', $firstIndent + 1, $firstIndent + 5);
571 $loose = $input->skipSpace();
572 $line = $input->currentLine();
574 if (!$line || !preg_match($pat, $line, $mm)) {
575 $input->setPos($pos);
576 return false; // No body found.
579 $indent = strlen($mm[0]);
580 $term = TransformInline(rtrim(substr(trim($m->match),0,-1)));
581 $defn = new TightSubBlock($input, sprintf("\\ {%d}", $indent), false, 'dd');
582 return array($term, $defn, $loose);
588 class Block_table_dl_defn extends XmlContent
593 function Block_table_dl_defn ($term, $defn) {
595 if (!is_array($defn))
596 $defn = $defn->getContent();
598 $this->_next_tight_top = false; // value irrelevant - gets fixed later
599 $this->_ncols = $this->_ComputeNcols($defn);
602 foreach ($defn as $item) {
603 if ($this->_IsASubtable($item))
604 $this->_addSubtable($item);
606 $this->_addToRow($item);
610 $th = HTML::th($term);
611 if ($this->_nrows > 1)
612 $th->setAttr('rowspan', $this->_nrows);
613 $this->_setTerm($th);
616 function setTightness($tight_top, $tight_bot) {
617 $this->_tight_top = $tight_top;
618 $this->_tight_bot = $tight_bot;
619 $first = &$this->firstTR();
620 $last = &$this->lastTR();
621 $first->setInClass('top', $tight_top);
623 $last->setInClass('bottom', $tight_bot);
625 trigger_error(sprintf("no lastTR: %s",AsXml($this->_content[0])), E_USER_WARNING);
629 function _addToRow ($item) {
630 if (empty($this->_accum)) {
631 $this->_accum = HTML::td();
632 if ($this->_ncols > 2)
633 $this->_accum->setAttr('colspan', $this->_ncols - 1);
635 $this->_accum->pushContent($item);
638 function _flushRow ($tight_bottom=false) {
639 if (!empty($this->_accum)) {
640 $row = new Block_HtmlElement('tr', false, $this->_accum);
642 $row->setTightness($this->_next_tight_top, $tight_bottom);
643 $this->_next_tight_top = $tight_bottom;
645 $this->pushContent($row);
646 $this->_accum = false;
651 function _addSubtable ($table) {
652 if (!($table_rows = $table->getContent()))
655 $this->_flushRow($table_rows[0]->_tight_top);
657 foreach ($table_rows as $subdef) {
658 $this->pushContent($subdef);
659 $this->_nrows += $subdef->nrows();
660 $this->_next_tight_top = $subdef->_tight_bot;
664 function _setTerm ($th) {
665 $first_row = &$this->_content[0];
666 if (isa($first_row, 'Block_table_dl_defn'))
667 $first_row->_setTerm($th);
669 $first_row->unshiftContent($th);
672 function _ComputeNcols ($defn) {
674 foreach ($defn as $item) {
675 if ($this->_IsASubtable($item)) {
676 $row = $this->_FirstDefn($item);
677 $ncols = max($ncols, $row->ncols() + 1);
683 function _IsASubtable ($item) {
684 return isa($item, 'HtmlElement')
685 && $item->getTag() == 'table'
686 && $item->getAttr('class') == 'wiki-dl-table';
689 function _FirstDefn ($subtable) {
690 $defs = $subtable->getContent();
695 return $this->_ncols;
699 return $this->_nrows;
702 function & firstTR() {
703 $first = &$this->_content[0];
704 if (isa($first, 'Block_table_dl_defn'))
705 return $first->firstTR();
709 function & lastTR() {
710 $last = &$this->_content[$this->_nrows - 1];
711 if (isa($last, 'Block_table_dl_defn'))
712 return $last->lastTR();
716 function setWidth ($ncols) {
717 assert($ncols >= $this->_ncols);
718 if ($ncols <= $this->_ncols)
720 $rows = &$this->_content;
721 for ($i = 0; $i < count($rows); $i++) {
723 if (isa($row, 'Block_table_dl_defn'))
724 $row->setWidth($ncols - 1);
726 $n = count($row->_content);
727 $lastcol = &$row->_content[$n - 1];
728 if (!empty($lastcol))
729 $lastcol->setAttr('colspan', $ncols - 1);
735 class Block_table_dl extends Block_dl
737 var $_tag = 'dl-table'; // phony.
739 function Block_table_dl() {
740 $this->_re = '\ {0,4} (?:\S.*)? (?<!'.ESCAPE_CHAR.') \| \s* $';
743 function _match (&$input, $m) {
744 if (!($p = $this->_do_match($input, $m)))
746 list ($term, $defn, $loose) = $p;
748 $this->_content[] = new Block_table_dl_defn($term, $defn);
752 function _setTightness($top, $bot) {
753 $this->_content[0]->setTightness($top, $bot);
758 $defs = &$this->_content;
761 foreach ($defs as $defn)
762 $ncols = max($ncols, $defn->ncols());
764 foreach ($defs as $key => $defn)
765 $defs[$key]->setWidth($ncols);
767 return HTML::table(array('class' => 'wiki-dl-table',
775 class Block_oldlists extends Block_list
777 //var $_tag = 'ol', 'ul', or 'dl';
778 var $_re = '(?: [*] (?!(?=\S)[^*]*(?<=\S)[*](?:\\s|[-)}>"\'\\/:.,;!?_*=]))
779 | [#] (?! \[ .*? \] )
783 function _match (&$input, $m) {
785 if (!preg_match('/[*#;]*$/A', $input->getPrefix())) {
791 $oldindent = '[*#;](?=[#*]|;.*:.*\S)';
792 $newindent = sprintf('\\ {%d}', strlen($prefix));
793 $indent = "(?:$oldindent|$newindent)";
795 $bullet = $prefix[0];
796 if ($bullet == '*') {
800 elseif ($bullet == '#') {
806 list ($term,) = explode(':', substr($prefix, 1), 2);
809 $this->_content[] = new Block_HtmlElement('dt', false,
810 TransformInline($term));
814 $this->_content[] = new TightSubBlock($input, $indent, $m->match, $itemtag);
818 function _setTightness($top, $bot) {
819 if (count($this->_content) == 1) {
820 $li = &$this->_content[0];
821 $li->setTightness($top, $bot);
824 // this is where php5 breaks
825 if (DEBUG && check_php_version(5)) {
826 if (function_exists("xdebug_get_function_stack")) {
827 var_dump (xdebug_get_function_stack());
828 } elseif (count($this->_content) != 2) {
831 $class = new Reflection_Class('XmlElement');
832 // Print out basic information
834 "===> The %s%s%s %s '%s' [extends %s]\n".
837 " having the modifiers %d [%s]\n",
838 $class->isInternal() ? 'internal' : 'user-defined',
839 $class->isAbstract() ? ' abstract' : '',
840 $class->isFinal() ? ' final' : '',
841 $class->isInterface() ? 'interface' : 'class',
843 var_export($class->getParentClass(), 1),
844 $class->getFileName(),
845 $class->getStartLine(),
846 $class->getEndline(),
847 $class->getModifiers(),
848 implode(' ', Reflection::getModifierNames($class->getModifiers()))
850 // Print class properties
851 printf("---> Properties: %s\n", var_export($class->getProperties(), 1));
853 echo 'count($this->_content): ', count($this->_content),"\n";
854 echo "\$this->_content[0]: "; var_dump ($this->_content[0]);
855 foreach ($this->_content as $c) {
856 echo "_tag: "; var_dump ($c->_tag);
857 echo "_content: "; var_dump ($c->_content);
858 echo "_properties: "; var_dump ($c->_properties);
860 debug_print_backtrace();
864 assert(count($this->_content) == 2);
865 $dt = &$this->_content[0];
866 $dd = &$this->_content[1];
867 $dt->setTightness($top, false);
868 $dd->setTightness(false, $bot);
873 class Block_pre extends BlockMarkup
875 var $_re = '<(?:pre|verbatim)>';
877 function _match (&$input, $m) {
878 $endtag = '</' . substr($m->match, 1);
880 $pos = $input->getPos();
882 $line = $m->postmatch;
883 while (ltrim($line) != $endtag) {
885 if (($line = $input->nextLine()) === false) {
886 $input->setPos($pos);
892 $text = join("\n", $text);
894 // FIXME: no <img>, <big>, <small>, <sup>, or <sub>'s allowed
896 if ($m->match == '<pre>')
897 $text = TransformInline($text);
899 $this->_element = new Block_HtmlElement('pre', false, $text);
905 class Block_plugin extends Block_pre
907 var $_re = '<\?plugin(?:-form)?(?!\S)';
910 /* <?plugin Backlinks
914 function _match (&$input, $m) {
915 $pos = $input->getPos();
916 $pi = $m->match . $m->postmatch;
917 while (!preg_match('/(?<!'.ESCAPE_CHAR.')\?>\s*$/', $pi)) {
918 if (($line = $input->nextLine()) === false) {
919 $input->setPos($pos);
926 $this->_element = new Cached_PluginInvocation($pi);
931 class Block_email_blockquote extends BlockMarkup
933 var $_attr = array('class' => 'mail-style-quote');
936 function _match (&$input, $m) {
937 //$indent = str_replace(' ', '\\ ', $m->match) . '|>$';
938 $indent = $this->_re;
939 $this->_element = new SubBlock($input, $indent, $m->match,
940 'blockquote', $this->_attr);
945 class Block_hr extends BlockMarkup
947 var $_re = '-{4,}\s*$';
949 function _match (&$input, $m) {
951 $this->_element = new Block_HtmlElement('hr');
955 function _setTightness($top, $bot) {
956 // Don't tighten <hr/>s
960 class Block_heading extends BlockMarkup
964 function _match (&$input, $m) {
965 $tag = "h" . (5 - strlen($m->match));
966 $text = TransformInline(trim($m->postmatch));
969 $this->_element = new Block_HtmlElement($tag, false, $text);
974 function _setTightness($top, $bot) {
975 // Don't tighten headers.
979 class Block_p extends BlockMarkup
984 function _match (&$input, $m) {
985 $this->_text = $m->match;
990 function _setTightness ($top, $bot) {
991 $this->_tight_top = $top;
992 $this->_tight_bot = $bot;
995 function merge ($nextBlock) {
996 $class = get_class($nextBlock);
997 if ($class == 'block_p' && $this->_tight_bot) {
998 $this->_text .= "\n" . $nextBlock->_text;
999 $this->_tight_bot = $nextBlock->_tight_bot;
1005 function finish () {
1006 $content = TransformInline(trim($this->_text));
1007 $p = new Block_HtmlElement('p', false, $content);
1008 $p->setTightness($this->_tight_top, $this->_tight_bot);
1013 ////////////////////////////////////////////////////////////////
1016 function TransformText ($text, $markup = 2.0, $basepage=false) {
1017 if (isa($text, 'WikiDB_PageRevision')) {
1019 $text = $rev->getPackedContent();
1020 $markup = $rev->get('markup');
1023 if (empty($markup) || $markup < 2.0) {
1024 //include_once("lib/transform.php");
1025 //return do_transform($text);
1026 $text = ConvertOldMarkup($text);
1029 // Expand leading tabs.
1030 $text = expand_tabs($text);
1032 //set_time_limit(3);
1034 $output = new WikiText($text);
1035 if (0 && DEBUG && check_php_version(5)) {
1036 echo "<pre>"; var_dump($output); echo "</pre>";
1040 // This is for immediate consumption.
1041 // We must bind the contents to a base pagename so that
1042 // relative page links can be properly linkified...
1043 return new CacheableMarkup($output->getContent(), $basepage);
1046 return new XmlContent($output->getContent());
1049 // (c-file-style: "gnu")
1053 // c-basic-offset: 4
1054 // c-hanging-comment-ender-p: nil
1055 // indent-tabs-mode: nil