WikiTransform(); // register functions // functions are applied in order of registering $this->register(WT_SIMPLE_MARKUP, 'wtm_plugin_link'); $this->register(WT_TOKENIZER, 'wtt_doublebrackets', '\[\['); //$this->register(WT_TOKENIZER, 'wtt_footnotes', '^\[\d+\]'); //$this->register(WT_TOKENIZER, 'wtt_footnoterefs', '\[\d+\]'); $this->register(WT_TOKENIZER, 'wtt_bracketlinks', '\[.+?\]'); $this->register(WT_TOKENIZER, 'wtt_urls', "!?\b($AllowedProtocols):[^\s<>\[\]\"'()]*[^\s<>\[\]\"'(),.?]"); if (function_exists('wtt_interwikilinks')) { $this->register(WT_TOKENIZER, 'wtt_interwikilinks', pcre_fix_posix_classes("!?(?register(WT_TOKENIZER, 'wtt_bumpylinks', "!?$WikiNameRegexp"); $this->register(WT_SIMPLE_MARKUP, 'wtm_htmlchars'); $this->register(WT_SIMPLE_MARKUP, 'wtm_linebreak'); $this->register(WT_SIMPLE_MARKUP, 'wtm_bold_italics'); } }; function TransformInline ($text) { $lines = preg_split('/[ \t\r]*\n/', trim($text)); $trfm = new InlineTransform; return $trfm->do_transform('', $lines); } /** * FIXME: * Still to do: * headings, * hr * tables * simple li's (vs compound li's) & (dd's). */ class BlockParser { function BlockParser ($block_types) { $this->_blockTypes = array(); foreach ($block_types as $type) $this->registerBlockType($type); } function registerBlockType ($class) { // FIXME: this is hackish. // It's because there seems to be no way to // call static members of $class. $prototype = new $class (false); $this->_blockTypes[] = $prototype; } function parse ($text) { $content = array(); // strip leading blank lines $text = preg_replace("/\s*\n/A", "", $text); while ($text) { $parsed = $this->_parseOne($text); assert (!is_array($parsed)); $content[] = $parsed; // strip blank lines $text = preg_replace("/\s*\n/A", "", $text); } return $content; } function _parseOne (&$text) { $block = $this->_grokBlockType($text); // determine block type from first line of text assert($block); $btext = $block->extractBlock($text); while ($nextBlock = $this->_grokBlockType($text)) { $next_text = $nextBlock->extractBlock($text); if (! $nextBlock->matches($btext . $next_text)) { // Can't combine blocks. $text = $next_text . $text; if ($nextBlock->getDepth() < $block->getDepth()) { //if (!isa($block, "Block_blockquote")) { if ($block->_tag != 'blockquote') { // FIXME: move this. $block = new Block_blockquote; assert(preg_match('/(?:\s*\n)?( *)(?=\S)/', $btext, $m)); $block->_init($m); } } break; } assert ($nextBlock->getDepth() <= $block->getDepth()); $block = $nextBlock; $btext .= $next_text; } if (0) { echo "BLOCK $block->_tag:
\n";
            echo htmlspecialchars($btext);
            echo "\n

\n"; } return $block->parse($btext); } function _grokBlockType ($text) { foreach ($this->_blockTypes as $type) { if (($block = $type->Match($text))) return $block; } return false; } } class Block { var $_match_re; var $_prefix_re; var $_block_re; var $_depth = 0; var $_tag; var $_attr = false; /* function Block ($match = false) { if ($match) $this->_init($match); } */ function _init ($match) { $qprefix = preg_quote($match[1], '/'); $this->_prefix_re = $qprefix; $this->_block_re = "(?:(?:\s*\n)?${qprefix}.*\n?)+"; $this->_depth = strlen($match[1]); } /** * (This should be a static member function...) */ function Match ($text) { if (! preg_match($this->_match_re, $text, $match)) return false; $block = $this; // Copy self. $block->_init($match); return $block; } function extractBlock (&$text) { $block_re = &$this->_block_re; assert(preg_match("/$block_re/Ami", $text, $m)); $text = substr($text, strlen($m[0])); return $m[0]; } function getDepth () { return $this->_depth; } function matches ($text) { $block_re = &$this->_block_re; return preg_match("/$block_re\$/Ai", $text); } function parse ($text) { assert ($this->matches($text)); // Strip block prefix from $text. $prefix = &$this->_prefix_re; $text = preg_replace("/^$prefix/m", "", $text); global $BlockParser; return $this->wrap($BlockParser->parse($text)); } // FIXME: rename function wrap($content) { // DEBUGGING: //if (is_array($content)) $content = join('', $content); //$content = preg_replace("/(?_tag:\n" . preg_replace("/^(?!\$)/m", " ", $content); return new HtmlElement($this->_tag, $this->_attr, $content); } } class ListBlock extends Block { var $_match_re = "/(?:\s*\n)?( *[*#] *(?=\S))/A"; /** * Get a regexp which matches the line prefix for * any
  • of the same type (ul/ol) and depth. */ function makeLiPrefixRegexp ($match) { return preg_quote($match[1], '/'); } /** * Get a regexp which matches the line prefix for * any
  • continuation lines. */ function makeContPrefixRegexp ($match) { return sprintf(" {%d}", strlen($match[1])); } } class Block_list extends ListBlock { function _init ($match) { $this->_tag = $this->grokListType($match); $liprefix = $this->makeLiPrefixRegexp($match); $cprefix = $this->makeContPrefixRegexp($match); preg_match('/^ */', $match[1], $m); $this->_depth = strlen($m[0]); $this->_prefix_re = false; // don't strip any prefix $this->_block_re = ( "(?:\s*\n)?" // leading blank lines. . "(?:" . "${liprefix}.*\n?" // first line . "(?:(?:\s*\n)?${cprefix}.*\n?)*" // continuation lines . ")+" ); } function grokListType ($match) { return preg_match("/#\s*\$/", $match[0]) ? 'ol' : 'ul'; } function parse ($text) { global $ListParser; return $this->wrap($ListParser->parse($text)); } } class Block_li extends ListBlock { var $_tag = 'li'; function _init ($match) { $liprefix = $this->makeLiPrefixRegexp($match); $cprefix = $this->makeContPrefixRegexp($match); $this->_prefix_re = "(?:${cprefix}|${liprefix})"; $this->_block_re = ( "${liprefix}.*\n?" // first line . "(?:(?:\s*\n)?${cprefix}.*\n?)*" ); // continuation lines } } class Block_dl extends Block { var $_tag = 'dl'; var $_match_re = "/(?:\s*\n)?[^\s*#].*:\s*\n +\S/A"; var $_prefix_re = false; // no prefix to strip var $_block_re = "(?:(?:\s*\n)?[^\s*#].*:(?:\s*\n +\S.*)+\n?)+"; function _init () { } function parse ($text) { $dt = new Block_dt; $dd = new Block_dd; $content = array(); while ($block = $dt->Match($text)) { $btext = $block->extractBlock($text); $content[] = $block->parse($btext); $block = $dd->Match($text); assert($block); $btext = $block->extractBlock($text); $content[] = $block->parse($btext); } assert(preg_match("/\s*\$/A", $text)); return $this->wrap($content); } } class Block_dt extends Block { var $_tag = 'dt'; var $_match_re = "/(?:\s*\n)?[^\s*#].*:\s*?\n/A"; var $_block_re = "(?:\s*\n)?[^\s*#].*:\s*\n"; function _init () { } function parse ($text) { assert(preg_match("/(\S.*?)\s*:/A", $text, $m)); return $this->wrap($m[1]); } } class Block_dd extends Block { var $_tag = 'dd'; var $_match_re = "/(?:(?:\s*\n)? +\S.*\n?)+/A"; var $_block_re = "(?:(?:\s*\n)? +\S.*\n?)+"; function _init ($match) { $indent = $this->_getIndent($match[0]); $this->_prefix_re = sprintf(" {%d}", $indent); } function _getIndent ($body) { assert(preg_match_all("/^ +(?=\S)/m", $body, $m)); $indent = strlen($m[0][0]); foreach ($m[0] as $pfx) $indent = min($indent, strlen($pfx)); return $indent; } } class Block_blockquote extends Block { var $_tag = 'blockquote'; var $_match_re = "/(?:\s*\n)?( +(?=\S))/A"; } class BlockBlock extends Block { var $_prefix_re = false; // no prefix to strip function BlockBlock ($begin_re, $end_re) { $this->_begin_re = $begin_re; $this->_end_re = $end_re; $this->_block_re = ( "(?:\s*\n)?" . $begin_re . "(?:.|\n)*?" . $end_re . "\s*?(?=\n|\S|$)" ); $this->_match_re = "/" . $this->_block_re . "/Ai"; } function _init () { } function _strip ($text) { $beg = $this->_begin_re; $end = $this->_end_re; $text = preg_replace("/.*?${beg}/Asi", "", $text); $text = preg_replace("/${end}.*?$/si", "", $text); return $text; } function parse ($text) { // FIXME: parse inline markup. return $this->wrap(TransformInline($this->_strip($text))); } } class Block_pre extends BlockBlock { var $_tag = 'pre'; function Block_pre () { $this->BlockBlock("
    ", "(?");
        }
    }
    
    class Block_plugin extends BlockBlock
    {
        var $_tag = 'div';
        
        function Block_plugin () {
            $this->BlockBlock("<\?plugin(?:-form)?\s", "\?>");
            $this->_attr = array('class' => 'plugin');
        }
    
        function parse ($text) {
            global $request;
            $loader = new WikiPluginLoader;
            return $this->wrap($loader->expandPI($text, $request));
        }
    }
    
    class Block_p extends Block
    {
        var $_tag = 'p';
        var $_match_re = "/(?=\S)/A";
        var $_prefix_re = false;    // no prefix to strip
        var $_block_re = "\S.*\n?(?:^(?!\<\?)[^\s*#].*\n?)*";
            
        function _init ($match) {
        }
    
        function parse ($text) {
            // FIXME: parse inline markup.
            return $this->wrap(TransformInline($text));
        }
    }
    
    $GLOBALS['BlockParser'] = new BlockParser(array('Block_dl',
                                                    'Block_list',
                                                    'Block_blockquote',
                                                    'Block_pre',
                                                    'Block_plugin',
                                                    'Block_p'));
    
    $GLOBALS['ListParser'] = new BlockParser(array('Block_li'));
    
    
    // (c-file-style: "gnu")
    // Local Variables:
    // mode: php
    // tab-width: 8
    // c-basic-offset: 4
    // c-hanging-comment-ender-p: nil
    // indent-tabs-mode: nil
    // End:   
    ?>