about summary refs log tree commit diff stats
path: root/wiki/inc/parser
diff options
context:
space:
mode:
authorahriman <ahriman@falte.red>2019-01-02 04:57:35 +0000
committerahriman <ahriman@falte.red>2019-01-02 04:57:35 +0000
commit2bd7f83a6495011ada78ca8a9f2af417caf01760 (patch)
treef9acdb7f09e011c65330ab993d4db3620787dbfb /wiki/inc/parser
parentbcb215c3a7e914d05f166846a33860e48bba64fb (diff)
downloadsite-2bd7f83a6495011ada78ca8a9f2af417caf01760.tar.gz
removed dokuwiki
Diffstat (limited to 'wiki/inc/parser')
-rw-r--r--wiki/inc/parser/code.php73
-rw-r--r--wiki/inc/parser/handler.php1811
-rw-r--r--wiki/inc/parser/lexer.php614
-rw-r--r--wiki/inc/parser/metadata.php694
-rw-r--r--wiki/inc/parser/parser.php1034
-rw-r--r--wiki/inc/parser/renderer.php883
-rw-r--r--wiki/inc/parser/xhtml.php1970
-rw-r--r--wiki/inc/parser/xhtmlsummary.php89
8 files changed, 0 insertions, 7168 deletions
diff --git a/wiki/inc/parser/code.php b/wiki/inc/parser/code.php
deleted file mode 100644
index f91f1d2..0000000
--- a/wiki/inc/parser/code.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-/**
- * A simple renderer that allows downloading of code and file snippets
- *
- * @author Andreas Gohr <andi@splitbrain.org>
- */
-if(!defined('DOKU_INC')) die('meh.');
-
-class Doku_Renderer_code extends Doku_Renderer {
-    var $_codeblock = 0;
-
-    /**
-     * Send the wanted code block to the browser
-     *
-     * When the correct block was found it exits the script.
-     *
-     * @param string $text
-     * @param string $language
-     * @param string $filename
-     */
-    function code($text, $language = null, $filename = '') {
-        global $INPUT;
-        if(!$language) $language = 'txt';
-        $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language);
-        if(!$filename) $filename = 'snippet.'.$language;
-        $filename = utf8_basename($filename);
-        $filename = utf8_stripspecials($filename, '_');
-
-        // send CRLF to Windows clients
-        if(strpos($INPUT->server->str('HTTP_USER_AGENT'), 'Windows') !== false) {
-            $text = str_replace("\n", "\r\n", $text);
-        }
-
-        if($this->_codeblock == $INPUT->str('codeblock')) {
-            header("Content-Type: text/plain; charset=utf-8");
-            header("Content-Disposition: attachment; filename=$filename");
-            header("X-Robots-Tag: noindex");
-            echo trim($text, "\r\n");
-            exit;
-        }
-
-        $this->_codeblock++;
-    }
-
-    /**
-     * Wraps around code()
-     *
-     * @param string $text
-     * @param string $language
-     * @param string $filename
-     */
-    function file($text, $language = null, $filename = '') {
-        $this->code($text, $language, $filename);
-    }
-
-    /**
-     * This should never be reached, if it is send a 404
-     */
-    function document_end() {
-        http_status(404);
-        echo '404 - Not found';
-        exit;
-    }
-
-    /**
-     * Return the format of the renderer
-     *
-     * @returns string 'code'
-     */
-    function getFormat() {
-        return 'code';
-    }
-}
diff --git a/wiki/inc/parser/handler.php b/wiki/inc/parser/handler.php
deleted file mode 100644
index 780c6cf..0000000
--- a/wiki/inc/parser/handler.php
+++ /dev/null
@@ -1,1811 +0,0 @@
-<?php
-if(!defined('DOKU_INC')) die('meh.');
-if (!defined('DOKU_PARSER_EOL')) define('DOKU_PARSER_EOL',"\n");   // add this to make handling test cases simpler
-
-class Doku_Handler {
-
-    var $Renderer = null;
-
-    var $CallWriter = null;
-
-    var $calls = array();
-
-    var $status = array(
-        'section' => false,
-        'doublequote' => 0,
-    );
-
-    var $rewriteBlocks = true;
-
-    function __construct() {
-        $this->CallWriter = new Doku_Handler_CallWriter($this);
-    }
-
-    /**
-     * @param string $handler
-     * @param mixed $args
-     * @param integer|string $pos
-     */
-    function _addCall($handler, $args, $pos) {
-        $call = array($handler,$args, $pos);
-        $this->CallWriter->writeCall($call);
-    }
-
-    function addPluginCall($plugin, $args, $state, $pos, $match) {
-        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
-        $this->CallWriter->writeCall($call);
-    }
-
-    function _finalize(){
-
-        $this->CallWriter->finalise();
-
-        if ( $this->status['section'] ) {
-            $last_call = end($this->calls);
-            array_push($this->calls,array('section_close',array(), $last_call[2]));
-        }
-
-        if ( $this->rewriteBlocks ) {
-            $B = new Doku_Handler_Block();
-            $this->calls = $B->process($this->calls);
-        }
-
-        trigger_event('PARSER_HANDLER_DONE',$this);
-
-        array_unshift($this->calls,array('document_start',array(),0));
-        $last_call = end($this->calls);
-        array_push($this->calls,array('document_end',array(),$last_call[2]));
-    }
-
-    /**
-     * fetch the current call and advance the pointer to the next one
-     *
-     * @return bool|mixed
-     */
-    function fetch() {
-        $call = current($this->calls);
-        if($call !== false) {
-            next($this->calls); //advance the pointer
-            return $call;
-        }
-        return false;
-    }
-
-
-    /**
-     * Special plugin handler
-     *
-     * This handler is called for all modes starting with 'plugin_'.
-     * An additional parameter with the plugin name is passed
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     *
-     * @param string|integer $match
-     * @param string|integer $state
-     * @param integer $pos
-     * @param $pluginname
-     *
-     * @return bool
-     */
-    function plugin($match, $state, $pos, $pluginname){
-        $data = array($match);
-        /** @var DokuWiki_Syntax_Plugin $plugin */
-        $plugin = plugin_load('syntax',$pluginname);
-        if($plugin != null){
-            $data = $plugin->handle($match, $state, $pos, $this);
-        }
-        if ($data !== false) {
-            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
-        }
-        return true;
-    }
-
-    function base($match, $state, $pos) {
-        switch ( $state ) {
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('cdata',array($match), $pos);
-                return true;
-            break;
-        }
-    }
-
-    function header($match, $state, $pos) {
-        // get level and title
-        $title = trim($match);
-        $level = 7 - strspn($title,'=');
-        if($level < 1) $level = 1;
-        $title = trim($title,'=');
-        $title = trim($title);
-
-        if ($this->status['section']) $this->_addCall('section_close',array(),$pos);
-
-        $this->_addCall('header',array($title,$level,$pos), $pos);
-
-        $this->_addCall('section_open',array($level),$pos);
-        $this->status['section'] = true;
-        return true;
-    }
-
-    function notoc($match, $state, $pos) {
-        $this->_addCall('notoc',array(),$pos);
-        return true;
-    }
-
-    function nocache($match, $state, $pos) {
-        $this->_addCall('nocache',array(),$pos);
-        return true;
-    }
-
-    function linebreak($match, $state, $pos) {
-        $this->_addCall('linebreak',array(),$pos);
-        return true;
-    }
-
-    function eol($match, $state, $pos) {
-        $this->_addCall('eol',array(),$pos);
-        return true;
-    }
-
-    function hr($match, $state, $pos) {
-        $this->_addCall('hr',array(),$pos);
-        return true;
-    }
-
-    /**
-     * @param string|integer $match
-     * @param string|integer $state
-     * @param integer $pos
-     * @param string $name
-     */
-    function _nestingTag($match, $state, $pos, $name) {
-        switch ( $state ) {
-            case DOKU_LEXER_ENTER:
-                $this->_addCall($name.'_open', array(), $pos);
-            break;
-            case DOKU_LEXER_EXIT:
-                $this->_addCall($name.'_close', array(), $pos);
-            break;
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('cdata',array($match), $pos);
-            break;
-        }
-    }
-
-    function strong($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'strong');
-        return true;
-    }
-
-    function emphasis($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'emphasis');
-        return true;
-    }
-
-    function underline($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'underline');
-        return true;
-    }
-
-    function monospace($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'monospace');
-        return true;
-    }
-
-    function subscript($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'subscript');
-        return true;
-    }
-
-    function superscript($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'superscript');
-        return true;
-    }
-
-    function deleted($match, $state, $pos) {
-        $this->_nestingTag($match, $state, $pos, 'deleted');
-        return true;
-    }
-
-
-    function footnote($match, $state, $pos) {
-//        $this->_nestingTag($match, $state, $pos, 'footnote');
-        if (!isset($this->_footnote)) $this->_footnote = false;
-
-        switch ( $state ) {
-            case DOKU_LEXER_ENTER:
-                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
-                // we will still enter a new footnote mode, we just do nothing
-                if ($this->_footnote) {
-                    $this->_addCall('cdata',array($match), $pos);
-                    break;
-                }
-
-                $this->_footnote = true;
-
-                $ReWriter = new Doku_Handler_Nest($this->CallWriter,'footnote_close');
-                $this->CallWriter = & $ReWriter;
-                $this->_addCall('footnote_open', array(), $pos);
-            break;
-            case DOKU_LEXER_EXIT:
-                // check whether we have already exitted the footnote mode, can happen if the modes were nested
-                if (!$this->_footnote) {
-                    $this->_addCall('cdata',array($match), $pos);
-                    break;
-                }
-
-                $this->_footnote = false;
-
-                $this->_addCall('footnote_close', array(), $pos);
-                $this->CallWriter->process();
-                $ReWriter = & $this->CallWriter;
-                $this->CallWriter = & $ReWriter->CallWriter;
-            break;
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('cdata', array($match), $pos);
-            break;
-        }
-        return true;
-    }
-
-    function listblock($match, $state, $pos) {
-        switch ( $state ) {
-            case DOKU_LEXER_ENTER:
-                $ReWriter = new Doku_Handler_List($this->CallWriter);
-                $this->CallWriter = & $ReWriter;
-                $this->_addCall('list_open', array($match), $pos);
-            break;
-            case DOKU_LEXER_EXIT:
-                $this->_addCall('list_close', array(), $pos);
-                $this->CallWriter->process();
-                $ReWriter = & $this->CallWriter;
-                $this->CallWriter = & $ReWriter->CallWriter;
-            break;
-            case DOKU_LEXER_MATCHED:
-                $this->_addCall('list_item', array($match), $pos);
-            break;
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('cdata', array($match), $pos);
-            break;
-        }
-        return true;
-    }
-
-    function unformatted($match, $state, $pos) {
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $this->_addCall('unformatted',array($match), $pos);
-        }
-        return true;
-    }
-
-    function php($match, $state, $pos) {
-        global $conf;
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $this->_addCall('php',array($match), $pos);
-        }
-        return true;
-    }
-
-    function phpblock($match, $state, $pos) {
-        global $conf;
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $this->_addCall('phpblock',array($match), $pos);
-        }
-        return true;
-    }
-
-    function html($match, $state, $pos) {
-        global $conf;
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $this->_addCall('html',array($match), $pos);
-        }
-        return true;
-    }
-
-    function htmlblock($match, $state, $pos) {
-        global $conf;
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $this->_addCall('htmlblock',array($match), $pos);
-        }
-        return true;
-    }
-
-    function preformatted($match, $state, $pos) {
-        switch ( $state ) {
-            case DOKU_LEXER_ENTER:
-                $ReWriter = new Doku_Handler_Preformatted($this->CallWriter);
-                $this->CallWriter = $ReWriter;
-                $this->_addCall('preformatted_start',array(), $pos);
-            break;
-            case DOKU_LEXER_EXIT:
-                $this->_addCall('preformatted_end',array(), $pos);
-                $this->CallWriter->process();
-                $ReWriter = & $this->CallWriter;
-                $this->CallWriter = & $ReWriter->CallWriter;
-            break;
-            case DOKU_LEXER_MATCHED:
-                $this->_addCall('preformatted_newline',array(), $pos);
-            break;
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('preformatted_content',array($match), $pos);
-            break;
-        }
-
-        return true;
-    }
-
-    function quote($match, $state, $pos) {
-
-        switch ( $state ) {
-
-            case DOKU_LEXER_ENTER:
-                $ReWriter = new Doku_Handler_Quote($this->CallWriter);
-                $this->CallWriter = & $ReWriter;
-                $this->_addCall('quote_start',array($match), $pos);
-            break;
-
-            case DOKU_LEXER_EXIT:
-                $this->_addCall('quote_end',array(), $pos);
-                $this->CallWriter->process();
-                $ReWriter = & $this->CallWriter;
-                $this->CallWriter = & $ReWriter->CallWriter;
-            break;
-
-            case DOKU_LEXER_MATCHED:
-                $this->_addCall('quote_newline',array($match), $pos);
-            break;
-
-            case DOKU_LEXER_UNMATCHED:
-                $this->_addCall('cdata',array($match), $pos);
-            break;
-
-        }
-
-        return true;
-    }
-
-    /**
-     * Internal function for parsing highlight options.
-     * $options is parsed for key value pairs separated by commas.
-     * A value might also be missing in which case the value will simple
-     * be set to true. Commas in strings are ignored, e.g. option="4,56"
-     * will work as expected and will only create one entry.
-     *
-     * @param string $options space separated list of key-value pairs,
-     *                        e.g. option1=123, option2="456"
-     * @return array|null     Array of key-value pairs $array['key'] = 'value';
-     *                        or null if no entries found
-     */
-    protected function parse_highlight_options ($options) {
-        $result = array();
-        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
-        foreach ($matches as $match) {
-            $equal_sign = strpos($match [0], '=');
-            if ($equal_sign === false) {
-                $key = trim($match[0]);
-                $result [$key] = 1;
-            } else {
-                $key = substr($match[0], 0, $equal_sign);
-                $value = substr($match[0], $equal_sign+1);
-                $value = trim($value, '"');
-                if (strlen($value) > 0) {
-                    $result [$key] = $value;
-                } else {
-                    $result [$key] = 1;
-                }
-            }
-        }
-
-        // Check for supported options
-        $result = array_intersect_key(
-            $result,
-            array_flip(array(
-                'enable_line_numbers',
-                'start_line_numbers_at',
-                'highlight_lines_extra',
-                'enable_keyword_links')
-            )
-        );
-
-        // Sanitize values
-        if(isset($result['enable_line_numbers'])) {
-            if($result['enable_line_numbers'] === 'false') {
-                $result['enable_line_numbers'] = false;
-            }
-            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
-        }
-        if(isset($result['highlight_lines_extra'])) {
-            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
-            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
-            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
-        }
-        if(isset($result['start_line_numbers_at'])) {
-            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
-        }
-        if(isset($result['enable_keyword_links'])) {
-            if($result['enable_keyword_links'] === 'false') {
-                $result['enable_keyword_links'] = false;
-            }
-            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
-        }
-        if (count($result) == 0) {
-            return null;
-        }
-
-        return $result;
-    }
-
-    function file($match, $state, $pos) {
-        return $this->code($match, $state, $pos, 'file');
-    }
-
-    function code($match, $state, $pos, $type='code') {
-        if ( $state == DOKU_LEXER_UNMATCHED ) {
-            $matches = explode('>',$match,2);
-            // Cut out variable options enclosed in []
-            preg_match('/\[.*\]/', $matches[0], $options);
-            if (!empty($options[0])) {
-                $matches[0] = str_replace($options[0], '', $matches[0]);
-            }
-            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
-            while(count($param) < 2) array_push($param, null);
-            // We shortcut html here.
-            if ($param[0] == 'html') $param[0] = 'html4strict';
-            if ($param[0] == '-') $param[0] = null;
-            array_unshift($param, $matches[1]);
-            if (!empty($options[0])) {
-                $param [] = $this->parse_highlight_options ($options[0]);
-            }
-            $this->_addCall($type, $param, $pos);
-        }
-        return true;
-    }
-
-    function acronym($match, $state, $pos) {
-        $this->_addCall('acronym',array($match), $pos);
-        return true;
-    }
-
-    function smiley($match, $state, $pos) {
-        $this->_addCall('smiley',array($match), $pos);
-        return true;
-    }
-
-    function wordblock($match, $state, $pos) {
-        $this->_addCall('wordblock',array($match), $pos);
-        return true;
-    }
-
-    function entity($match, $state, $pos) {
-        $this->_addCall('entity',array($match), $pos);
-        return true;
-    }
-
-    function multiplyentity($match, $state, $pos) {
-        preg_match_all('/\d+/',$match,$matches);
-        $this->_addCall('multiplyentity',array($matches[0][0],$matches[0][1]), $pos);
-        return true;
-    }
-
-    function singlequoteopening($match, $state, $pos) {
-        $this->_addCall('singlequoteopening',array(), $pos);
-        return true;
-    }
-
-    function singlequoteclosing($match, $state, $pos) {
-        $this->_addCall('singlequoteclosing',array(), $pos);
-        return true;
-    }
-
-    function apostrophe($match, $state, $pos) {
-        $this->_addCall('apostrophe',array(), $pos);
-        return true;
-    }
-
-    function doublequoteopening($match, $state, $pos) {
-        $this->_addCall('doublequoteopening',array(), $pos);
-        $this->status['doublequote']++;
-        return true;
-    }
-
-    function doublequoteclosing($match, $state, $pos) {
-        if ($this->status['doublequote'] <= 0) {
-            $this->doublequoteopening($match, $state, $pos);
-        } else {
-            $this->_addCall('doublequoteclosing',array(), $pos);
-            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
-        }
-        return true;
-    }
-
-    function camelcaselink($match, $state, $pos) {
-        $this->_addCall('camelcaselink',array($match), $pos);
-        return true;
-    }
-
-    /*
-    */
-    function internallink($match, $state, $pos) {
-        // Strip the opening and closing markup
-        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
-
-        // Split title from URL
-        $link = explode('|',$link,2);
-        if ( !isset($link[1]) ) {
-            $link[1] = null;
-        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
-            // If the title is an image, convert it to an array containing the image details
-            $link[1] = Doku_Handler_Parse_Media($link[1]);
-        }
-        $link[0] = trim($link[0]);
-
-        //decide which kind of link it is
-
-        if ( link_isinterwiki($link[0]) ) {
-            // Interwiki
-            $interwiki = explode('>',$link[0],2);
-            $this->_addCall(
-                'interwikilink',
-                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
-                $pos
-                );
-        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
-            // Windows Share
-            $this->_addCall(
-                'windowssharelink',
-                array($link[0],$link[1]),
-                $pos
-                );
-        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
-            // external link (accepts all protocols)
-            $this->_addCall(
-                    'externallink',
-                    array($link[0],$link[1]),
-                    $pos
-                    );
-        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
-            // E-Mail (pattern above is defined in inc/mail.php)
-            $this->_addCall(
-                'emaillink',
-                array($link[0],$link[1]),
-                $pos
-                );
-        }elseif ( preg_match('!^#.+!',$link[0]) ){
-            // local link
-            $this->_addCall(
-                'locallink',
-                array(substr($link[0],1),$link[1]),
-                $pos
-                );
-        }else{
-            // internal link
-            $this->_addCall(
-                'internallink',
-                array($link[0],$link[1]),
-                $pos
-                );
-        }
-
-        return true;
-    }
-
-    function filelink($match, $state, $pos) {
-        $this->_addCall('filelink',array($match, null), $pos);
-        return true;
-    }
-
-    function windowssharelink($match, $state, $pos) {
-        $this->_addCall('windowssharelink',array($match, null), $pos);
-        return true;
-    }
-
-    function media($match, $state, $pos) {
-        $p = Doku_Handler_Parse_Media($match);
-
-        $this->_addCall(
-              $p['type'],
-              array($p['src'], $p['title'], $p['align'], $p['width'],
-                     $p['height'], $p['cache'], $p['linking']),
-              $pos
-             );
-        return true;
-    }
-
-    function rss($match, $state, $pos) {
-        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
-
-        // get params
-        list($link,$params) = explode(' ',$link,2);
-
-        $p = array();
-        if(preg_match('/\b(\d+)\b/',$params,$match)){
-            $p['max'] = $match[1];
-        }else{
-            $p['max'] = 8;
-        }
-        $p['reverse'] = (preg_match('/rev/',$params));
-        $p['author']  = (preg_match('/\b(by|author)/',$params));
-        $p['date']    = (preg_match('/\b(date)/',$params));
-        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
-        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
-
-        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
-            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
-            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
-        } else {
-            $p['refresh'] = 14400;   // default to 4 hours
-        }
-
-        $this->_addCall('rss',array($link,$p),$pos);
-        return true;
-    }
-
-    function externallink($match, $state, $pos) {
-        $url   = $match;
-        $title = null;
-
-        // add protocol on simple short URLs
-        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
-            $title = $url;
-            $url   = 'ftp://'.$url;
-        }
-        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
-            $title = $url;
-            $url = 'http://'.$url;
-        }
-
-        $this->_addCall('externallink',array($url, $title), $pos);
-        return true;
-    }
-
-    function emaillink($match, $state, $pos) {
-        $email = preg_replace(array('/^</','/>$/'),'',$match);
-        $this->_addCall('emaillink',array($email, null), $pos);
-        return true;
-    }
-
-    function table($match, $state, $pos) {
-        switch ( $state ) {
-
-            case DOKU_LEXER_ENTER:
-
-                $ReWriter = new Doku_Handler_Table($this->CallWriter);
-                $this->CallWriter = & $ReWriter;
-
-                $this->_addCall('table_start', array($pos + 1), $pos);
-                if ( trim($match) == '^' ) {
-                    $this->_addCall('tableheader', array(), $pos);
-                } else {
-                    $this->_addCall('tablecell', array(), $pos);
-                }
-            break;
-
-            case DOKU_LEXER_EXIT:
-                $this->_addCall('table_end', array($pos), $pos);
-                $this->CallWriter->process();
-                $ReWriter = & $this->CallWriter;
-                $this->CallWriter = & $ReWriter->CallWriter;
-            break;
-
-            case DOKU_LEXER_UNMATCHED:
-                if ( trim($match) != '' ) {
-                    $this->_addCall('cdata',array($match), $pos);
-                }
-            break;
-
-            case DOKU_LEXER_MATCHED:
-                if ( $match == ' ' ){
-                    $this->_addCall('cdata', array($match), $pos);
-                } else if ( preg_match('/:::/',$match) ) {
-                    $this->_addCall('rowspan', array($match), $pos);
-                } else if ( preg_match('/\t+/',$match) ) {
-                    $this->_addCall('table_align', array($match), $pos);
-                } else if ( preg_match('/ {2,}/',$match) ) {
-                    $this->_addCall('table_align', array($match), $pos);
-                } else if ( $match == "\n|" ) {
-                    $this->_addCall('table_row', array(), $pos);
-                    $this->_addCall('tablecell', array(), $pos);
-                } else if ( $match == "\n^" ) {
-                    $this->_addCall('table_row', array(), $pos);
-                    $this->_addCall('tableheader', array(), $pos);
-                } else if ( $match == '|' ) {
-                    $this->_addCall('tablecell', array(), $pos);
-                } else if ( $match == '^' ) {
-                    $this->_addCall('tableheader', array(), $pos);
-                }
-            break;
-        }
-        return true;
-    }
-}
-
-//------------------------------------------------------------------------
-function Doku_Handler_Parse_Media($match) {
-
-    // Strip the opening and closing markup
-    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
-
-    // Split title from URL
-    $link = explode('|',$link,2);
-
-    // Check alignment
-    $ralign = (bool)preg_match('/^ /',$link[0]);
-    $lalign = (bool)preg_match('/ $/',$link[0]);
-
-    // Logic = what's that ;)...
-    if ( $lalign & $ralign ) {
-        $align = 'center';
-    } else if ( $ralign ) {
-        $align = 'right';
-    } else if ( $lalign ) {
-        $align = 'left';
-    } else {
-        $align = null;
-    }
-
-    // The title...
-    if ( !isset($link[1]) ) {
-        $link[1] = null;
-    }
-
-    //remove aligning spaces
-    $link[0] = trim($link[0]);
-
-    //split into src and parameters (using the very last questionmark)
-    $pos = strrpos($link[0], '?');
-    if($pos !== false){
-        $src   = substr($link[0],0,$pos);
-        $param = substr($link[0],$pos+1);
-    }else{
-        $src   = $link[0];
-        $param = '';
-    }
-
-    //parse width and height
-    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
-        !empty($size[1]) ? $w = $size[1] : $w = null;
-        !empty($size[3]) ? $h = $size[3] : $h = null;
-    } else {
-        $w = null;
-        $h = null;
-    }
-
-    //get linking command
-    if(preg_match('/nolink/i',$param)){
-        $linking = 'nolink';
-    }else if(preg_match('/direct/i',$param)){
-        $linking = 'direct';
-    }else if(preg_match('/linkonly/i',$param)){
-        $linking = 'linkonly';
-    }else{
-        $linking = 'details';
-    }
-
-    //get caching command
-    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
-        $cache = $cachemode[1];
-    }else{
-        $cache = 'cache';
-    }
-
-    // Check whether this is a local or remote image or interwiki
-    if (media_isexternal($src) || link_isinterwiki($src)){
-        $call = 'externalmedia';
-    } else {
-        $call = 'internalmedia';
-    }
-
-    $params = array(
-        'type'=>$call,
-        'src'=>$src,
-        'title'=>$link[1],
-        'align'=>$align,
-        'width'=>$w,
-        'height'=>$h,
-        'cache'=>$cache,
-        'linking'=>$linking,
-    );
-
-    return $params;
-}
-
-//------------------------------------------------------------------------
-interface Doku_Handler_CallWriter_Interface {
-    public function writeCall($call);
-    public function writeCalls($calls);
-    public function finalise();
-}
-
-class Doku_Handler_CallWriter implements Doku_Handler_CallWriter_Interface {
-
-    var $Handler;
-
-    /**
-     * @param Doku_Handler $Handler
-     */
-    function __construct(Doku_Handler $Handler) {
-        $this->Handler = $Handler;
-    }
-
-    function writeCall($call) {
-        $this->Handler->calls[] = $call;
-    }
-
-    function writeCalls($calls) {
-        $this->Handler->calls = array_merge($this->Handler->calls, $calls);
-    }
-
-    // function is required, but since this call writer is first/highest in
-    // the chain it is not required to do anything
-    function finalise() {
-        unset($this->Handler);
-    }
-}
-
-//------------------------------------------------------------------------
-/**
- * Generic call writer class to handle nesting of rendering instructions
- * within a render instruction. Also see nest() method of renderer base class
- *
- * @author    Chris Smith <chris@jalakai.co.uk>
- */
-class Doku_Handler_Nest implements Doku_Handler_CallWriter_Interface {
-
-    var $CallWriter;
-    var $calls = array();
-
-    var $closingInstruction;
-
-    /**
-     * constructor
-     *
-     * @param  Doku_Handler_CallWriter $CallWriter     the renderers current call writer
-     * @param  string     $close          closing instruction name, this is required to properly terminate the
-     *                                    syntax mode if the document ends without a closing pattern
-     */
-    function __construct(Doku_Handler_CallWriter_Interface $CallWriter, $close="nest_close") {
-        $this->CallWriter = $CallWriter;
-
-        $this->closingInstruction = $close;
-    }
-
-    function writeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    function writeCalls($calls) {
-        $this->calls = array_merge($this->calls, $calls);
-    }
-
-    function finalise() {
-        $last_call = end($this->calls);
-        $this->writeCall(array($this->closingInstruction,array(), $last_call[2]));
-
-        $this->process();
-        $this->CallWriter->finalise();
-        unset($this->CallWriter);
-    }
-
-    function process() {
-        // merge consecutive cdata
-        $unmerged_calls = $this->calls;
-        $this->calls = array();
-
-        foreach ($unmerged_calls as $call) $this->addCall($call);
-
-        $first_call = reset($this->calls);
-        $this->CallWriter->writeCall(array("nest", array($this->calls), $first_call[2]));
-    }
-
-    function addCall($call) {
-        $key = count($this->calls);
-        if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
-            $this->calls[$key-1][1][0] .= $call[1][0];
-        } else if ($call[0] == 'eol') {
-            // do nothing (eol shouldn't be allowed, to counter preformatted fix in #1652 & #1699)
-        } else {
-            $this->calls[] = $call;
-        }
-    }
-}
-
-class Doku_Handler_List implements Doku_Handler_CallWriter_Interface {
-
-    var $CallWriter;
-
-    var $calls = array();
-    var $listCalls = array();
-    var $listStack = array();
-
-    const NODE = 1;
-
-    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
-        $this->CallWriter = $CallWriter;
-    }
-
-    function writeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    // Probably not needed but just in case...
-    function writeCalls($calls) {
-        $this->calls = array_merge($this->calls, $calls);
-#        $this->CallWriter->writeCalls($this->calls);
-    }
-
-    function finalise() {
-        $last_call = end($this->calls);
-        $this->writeCall(array('list_close',array(), $last_call[2]));
-
-        $this->process();
-        $this->CallWriter->finalise();
-        unset($this->CallWriter);
-    }
-
-    //------------------------------------------------------------------------
-    function process() {
-
-        foreach ( $this->calls as $call ) {
-            switch ($call[0]) {
-                case 'list_item':
-                    $this->listOpen($call);
-                break;
-                case 'list_open':
-                    $this->listStart($call);
-                break;
-                case 'list_close':
-                    $this->listEnd($call);
-                break;
-                default:
-                    $this->listContent($call);
-                break;
-            }
-        }
-
-        $this->CallWriter->writeCalls($this->listCalls);
-    }
-
-    //------------------------------------------------------------------------
-    function listStart($call) {
-        $depth = $this->interpretSyntax($call[1][0], $listType);
-
-        $this->initialDepth = $depth;
-        //                   array(list type, current depth, index of current listitem_open)
-        $this->listStack[] = array($listType, $depth, 1);
-
-        $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]);
-        $this->listCalls[] = array('listitem_open',array(1),$call[2]);
-        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-    }
-
-    //------------------------------------------------------------------------
-    function listEnd($call) {
-        $closeContent = true;
-
-        while ( $list = array_pop($this->listStack) ) {
-            if ( $closeContent ) {
-                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
-                $closeContent = false;
-            }
-            $this->listCalls[] = array('listitem_close',array(),$call[2]);
-            $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]);
-        }
-    }
-
-    //------------------------------------------------------------------------
-    function listOpen($call) {
-        $depth = $this->interpretSyntax($call[1][0], $listType);
-        $end = end($this->listStack);
-        $key = key($this->listStack);
-
-        // Not allowed to be shallower than initialDepth
-        if ( $depth < $this->initialDepth ) {
-            $depth = $this->initialDepth;
-        }
-
-        //------------------------------------------------------------------------
-        if ( $depth == $end[1] ) {
-
-            // Just another item in the list...
-            if ( $listType == $end[0] ) {
-                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
-                $this->listCalls[] = array('listitem_close',array(),$call[2]);
-                $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
-                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-
-                // new list item, update list stack's index into current listitem_open
-                $this->listStack[$key][2] = count($this->listCalls) - 2;
-
-            // Switched list type...
-            } else {
-
-                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
-                $this->listCalls[] = array('listitem_close',array(),$call[2]);
-                $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
-                $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
-                $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
-                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-
-                array_pop($this->listStack);
-                $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
-            }
-
-        //------------------------------------------------------------------------
-        // Getting deeper...
-        } else if ( $depth > $end[1] ) {
-
-            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
-            $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
-            $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
-            $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-
-            // set the node/leaf state of this item's parent listitem_open to NODE
-            $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE;
-
-            $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
-
-        //------------------------------------------------------------------------
-        // Getting shallower ( $depth < $end[1] )
-        } else {
-            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
-            $this->listCalls[] = array('listitem_close',array(),$call[2]);
-            $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
-
-            // Throw away the end - done
-            array_pop($this->listStack);
-
-            while (1) {
-                $end = end($this->listStack);
-                $key = key($this->listStack);
-
-                if ( $end[1] <= $depth ) {
-
-                    // Normalize depths
-                    $depth = $end[1];
-
-                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
-
-                    if ( $end[0] == $listType ) {
-                        $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
-                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-
-                        // new list item, update list stack's index into current listitem_open
-                        $this->listStack[$key][2] = count($this->listCalls) - 2;
-
-                    } else {
-                        // Switching list type...
-                        $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
-                        $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
-                        $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
-                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
-
-                        array_pop($this->listStack);
-                        $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
-                    }
-
-                    break;
-
-                // Haven't dropped down far enough yet.... ( $end[1] > $depth )
-                } else {
-
-                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
-                    $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
-
-                    array_pop($this->listStack);
-
-                }
-
-            }
-
-        }
-    }
-
-    //------------------------------------------------------------------------
-    function listContent($call) {
-        $this->listCalls[] = $call;
-    }
-
-    //------------------------------------------------------------------------
-    function interpretSyntax($match, & $type) {
-        if ( substr($match,-1) == '*' ) {
-            $type = 'u';
-        } else {
-            $type = 'o';
-        }
-        // Is the +1 needed? It used to be count(explode(...))
-        // but I don't think the number is seen outside this handler
-        return substr_count(str_replace("\t",'  ',$match), '  ') + 1;
-    }
-}
-
-//------------------------------------------------------------------------
-class Doku_Handler_Preformatted implements Doku_Handler_CallWriter_Interface {
-
-    var $CallWriter;
-
-    var $calls = array();
-    var $pos;
-    var $text ='';
-
-
-
-    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
-        $this->CallWriter = $CallWriter;
-    }
-
-    function writeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    // Probably not needed but just in case...
-    function writeCalls($calls) {
-        $this->calls = array_merge($this->calls, $calls);
-#        $this->CallWriter->writeCalls($this->calls);
-    }
-
-    function finalise() {
-        $last_call = end($this->calls);
-        $this->writeCall(array('preformatted_end',array(), $last_call[2]));
-
-        $this->process();
-        $this->CallWriter->finalise();
-        unset($this->CallWriter);
-    }
-
-    function process() {
-        foreach ( $this->calls as $call ) {
-            switch ($call[0]) {
-                case 'preformatted_start':
-                    $this->pos = $call[2];
-                break;
-                case 'preformatted_newline':
-                    $this->text .= "\n";
-                break;
-                case 'preformatted_content':
-                    $this->text .= $call[1][0];
-                break;
-                case 'preformatted_end':
-                    if (trim($this->text)) {
-                        $this->CallWriter->writeCall(array('preformatted',array($this->text),$this->pos));
-                    }
-                    // see FS#1699 & FS#1652, add 'eol' instructions to ensure proper triggering of following p_open
-                    $this->CallWriter->writeCall(array('eol',array(),$this->pos));
-                    $this->CallWriter->writeCall(array('eol',array(),$this->pos));
-                break;
-            }
-        }
-    }
-
-}
-
-//------------------------------------------------------------------------
-class Doku_Handler_Quote implements Doku_Handler_CallWriter_Interface {
-
-    var $CallWriter;
-
-    var $calls = array();
-
-    var $quoteCalls = array();
-
-    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
-        $this->CallWriter = $CallWriter;
-    }
-
-    function writeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    // Probably not needed but just in case...
-    function writeCalls($calls) {
-        $this->calls = array_merge($this->calls, $calls);
-    }
-
-    function finalise() {
-        $last_call = end($this->calls);
-        $this->writeCall(array('quote_end',array(), $last_call[2]));
-
-        $this->process();
-        $this->CallWriter->finalise();
-        unset($this->CallWriter);
-    }
-
-    function process() {
-
-        $quoteDepth = 1;
-
-        foreach ( $this->calls as $call ) {
-            switch ($call[0]) {
-
-                case 'quote_start':
-
-                    $this->quoteCalls[] = array('quote_open',array(),$call[2]);
-
-                case 'quote_newline':
-
-                    $quoteLength = $this->getDepth($call[1][0]);
-
-                    if ( $quoteLength > $quoteDepth ) {
-                        $quoteDiff = $quoteLength - $quoteDepth;
-                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
-                            $this->quoteCalls[] = array('quote_open',array(),$call[2]);
-                        }
-                    } else if ( $quoteLength < $quoteDepth ) {
-                        $quoteDiff = $quoteDepth - $quoteLength;
-                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
-                            $this->quoteCalls[] = array('quote_close',array(),$call[2]);
-                        }
-                    } else {
-                        if ($call[0] != 'quote_start') $this->quoteCalls[] = array('linebreak',array(),$call[2]);
-                    }
-
-                    $quoteDepth = $quoteLength;
-
-                break;
-
-                case 'quote_end':
-
-                    if ( $quoteDepth > 1 ) {
-                        $quoteDiff = $quoteDepth - 1;
-                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
-                            $this->quoteCalls[] = array('quote_close',array(),$call[2]);
-                        }
-                    }
-
-                    $this->quoteCalls[] = array('quote_close',array(),$call[2]);
-
-                    $this->CallWriter->writeCalls($this->quoteCalls);
-                break;
-
-                default:
-                    $this->quoteCalls[] = $call;
-                break;
-            }
-        }
-    }
-
-    function getDepth($marker) {
-        preg_match('/>{1,}/', $marker, $matches);
-        $quoteLength = strlen($matches[0]);
-        return $quoteLength;
-    }
-}
-
-//------------------------------------------------------------------------
-class Doku_Handler_Table implements Doku_Handler_CallWriter_Interface {
-
-    var $CallWriter;
-
-    var $calls = array();
-    var $tableCalls = array();
-    var $maxCols = 0;
-    var $maxRows = 1;
-    var $currentCols = 0;
-    var $firstCell = false;
-    var $lastCellType = 'tablecell';
-    var $inTableHead = true;
-    var $currentRow = array('tableheader' => 0, 'tablecell' => 0);
-    var $countTableHeadRows = 0;
-
-    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
-        $this->CallWriter = $CallWriter;
-    }
-
-    function writeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    // Probably not needed but just in case...
-    function writeCalls($calls) {
-        $this->calls = array_merge($this->calls, $calls);
-    }
-
-    function finalise() {
-        $last_call = end($this->calls);
-        $this->writeCall(array('table_end',array(), $last_call[2]));
-
-        $this->process();
-        $this->CallWriter->finalise();
-        unset($this->CallWriter);
-    }
-
-    //------------------------------------------------------------------------
-    function process() {
-        foreach ( $this->calls as $call ) {
-            switch ( $call[0] ) {
-                case 'table_start':
-                    $this->tableStart($call);
-                break;
-                case 'table_row':
-                    $this->tableRowClose($call);
-                    $this->tableRowOpen(array('tablerow_open',$call[1],$call[2]));
-                break;
-                case 'tableheader':
-                case 'tablecell':
-                    $this->tableCell($call);
-                break;
-                case 'table_end':
-                    $this->tableRowClose($call);
-                    $this->tableEnd($call);
-                break;
-                default:
-                    $this->tableDefault($call);
-                break;
-            }
-        }
-        $this->CallWriter->writeCalls($this->tableCalls);
-    }
-
-    function tableStart($call) {
-        $this->tableCalls[] = array('table_open',$call[1],$call[2]);
-        $this->tableCalls[] = array('tablerow_open',array(),$call[2]);
-        $this->firstCell = true;
-    }
-
-    function tableEnd($call) {
-        $this->tableCalls[] = array('table_close',$call[1],$call[2]);
-        $this->finalizeTable();
-    }
-
-    function tableRowOpen($call) {
-        $this->tableCalls[] = $call;
-        $this->currentCols = 0;
-        $this->firstCell = true;
-        $this->lastCellType = 'tablecell';
-        $this->maxRows++;
-        if ($this->inTableHead) {
-            $this->currentRow = array('tablecell' => 0, 'tableheader' => 0);
-        }
-    }
-
-    function tableRowClose($call) {
-        if ($this->inTableHead && ($this->inTableHead = $this->isTableHeadRow())) {
-            $this->countTableHeadRows++;
-        }
-        // Strip off final cell opening and anything after it
-        while ( $discard = array_pop($this->tableCalls ) ) {
-
-            if ( $discard[0] == 'tablecell_open' || $discard[0] == 'tableheader_open') {
-                break;
-            }
-            if (!empty($this->currentRow[$discard[0]])) {
-                $this->currentRow[$discard[0]]--;
-            }
-        }
-        $this->tableCalls[] = array('tablerow_close', array(), $call[2]);
-
-        if ( $this->currentCols > $this->maxCols ) {
-            $this->maxCols = $this->currentCols;
-        }
-    }
-
-    function isTableHeadRow() {
-        $td = $this->currentRow['tablecell'];
-        $th = $this->currentRow['tableheader'];
-
-        if (!$th || $td > 2) return false;
-        if (2*$td > $th) return false;
-
-        return true;
-    }
-
-    function tableCell($call) {
-        if ($this->inTableHead) {
-            $this->currentRow[$call[0]]++;
-        }
-        if ( !$this->firstCell ) {
-
-            // Increase the span
-            $lastCall = end($this->tableCalls);
-
-            // A cell call which follows an open cell means an empty cell so span
-            if ( $lastCall[0] == 'tablecell_open' || $lastCall[0] == 'tableheader_open' ) {
-                 $this->tableCalls[] = array('colspan',array(),$call[2]);
-
-            }
-
-            $this->tableCalls[] = array($this->lastCellType.'_close',array(),$call[2]);
-            $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
-            $this->lastCellType = $call[0];
-
-        } else {
-
-            $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
-            $this->lastCellType = $call[0];
-            $this->firstCell = false;
-
-        }
-
-        $this->currentCols++;
-    }
-
-    function tableDefault($call) {
-        $this->tableCalls[] = $call;
-    }
-
-    function finalizeTable() {
-
-        // Add the max cols and rows to the table opening
-        if ( $this->tableCalls[0][0] == 'table_open' ) {
-            // Adjust to num cols not num col delimeters
-            $this->tableCalls[0][1][] = $this->maxCols - 1;
-            $this->tableCalls[0][1][] = $this->maxRows;
-            $this->tableCalls[0][1][] = array_shift($this->tableCalls[0][1]);
-        } else {
-            trigger_error('First element in table call list is not table_open');
-        }
-
-        $lastRow = 0;
-        $lastCell = 0;
-        $cellKey = array();
-        $toDelete = array();
-
-        // if still in tableheader, then there can be no table header
-        // as all rows can't be within <THEAD>
-        if ($this->inTableHead) {
-            $this->inTableHead = false;
-            $this->countTableHeadRows = 0;
-        }
-
-        // Look for the colspan elements and increment the colspan on the
-        // previous non-empty opening cell. Once done, delete all the cells
-        // that contain colspans
-        for ($key = 0 ; $key < count($this->tableCalls) ; ++$key) {
-            $call = $this->tableCalls[$key];
-
-            switch ($call[0]) {
-                case 'table_open' :
-                    if($this->countTableHeadRows) {
-                        array_splice($this->tableCalls, $key+1, 0, array(
-                              array('tablethead_open', array(), $call[2]))
-                        );
-                    }
-                    break;
-
-                case 'tablerow_open':
-
-                    $lastRow++;
-                    $lastCell = 0;
-                    break;
-
-                case 'tablecell_open':
-                case 'tableheader_open':
-
-                    $lastCell++;
-                    $cellKey[$lastRow][$lastCell] = $key;
-                    break;
-
-                case 'table_align':
-
-                    $prev = in_array($this->tableCalls[$key-1][0], array('tablecell_open', 'tableheader_open'));
-                    $next = in_array($this->tableCalls[$key+1][0], array('tablecell_close', 'tableheader_close'));
-                    // If the cell is empty, align left
-                    if ($prev && $next) {
-                        $this->tableCalls[$key-1][1][1] = 'left';
-
-                    // If the previous element was a cell open, align right
-                    } elseif ($prev) {
-                        $this->tableCalls[$key-1][1][1] = 'right';
-
-                    // If the next element is the close of an element, align either center or left
-                    } elseif ( $next) {
-                        if ( $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] == 'right' ) {
-                            $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'center';
-                        } else {
-                            $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'left';
-                        }
-
-                    }
-
-                    // Now convert the whitespace back to cdata
-                    $this->tableCalls[$key][0] = 'cdata';
-                    break;
-
-                case 'colspan':
-
-                    $this->tableCalls[$key-1][1][0] = false;
-
-                    for($i = $key-2; $i >= $cellKey[$lastRow][1]; $i--) {
-
-                        if ( $this->tableCalls[$i][0] == 'tablecell_open' || $this->tableCalls[$i][0] == 'tableheader_open' ) {
-
-                            if ( false !== $this->tableCalls[$i][1][0] ) {
-                                $this->tableCalls[$i][1][0]++;
-                                break;
-                            }
-
-                        }
-                    }
-
-                    $toDelete[] = $key-1;
-                    $toDelete[] = $key;
-                    $toDelete[] = $key+1;
-                    break;
-
-                case 'rowspan':
-
-                    if ( $this->tableCalls[$key-1][0] == 'cdata' ) {
-                        // ignore rowspan if previous call was cdata (text mixed with :::) we don't have to check next call as that wont match regex
-                        $this->tableCalls[$key][0] = 'cdata';
-
-                    } else {
-
-                        $spanning_cell = null;
-
-                        // can't cross thead/tbody boundary
-                        if (!$this->countTableHeadRows || ($lastRow-1 != $this->countTableHeadRows)) {
-                            for($i = $lastRow-1; $i > 0; $i--) {
-
-                                if ( $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tablecell_open' || $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tableheader_open' ) {
-
-                                    if ($this->tableCalls[$cellKey[$i][$lastCell]][1][2] >= $lastRow - $i) {
-                                        $spanning_cell = $i;
-                                        break;
-                                    }
-
-                                }
-                            }
-                        }
-                        if (is_null($spanning_cell)) {
-                            // No spanning cell found, so convert this cell to
-                            // an empty one to avoid broken tables
-                            $this->tableCalls[$key][0] = 'cdata';
-                            $this->tableCalls[$key][1][0] = '';
-                            continue;
-                        }
-                        $this->tableCalls[$cellKey[$spanning_cell][$lastCell]][1][2]++;
-
-                        $this->tableCalls[$key-1][1][2] = false;
-
-                        $toDelete[] = $key-1;
-                        $toDelete[] = $key;
-                        $toDelete[] = $key+1;
-                    }
-                    break;
-
-                case 'tablerow_close':
-
-                    // Fix broken tables by adding missing cells
-                    $moreCalls = array();
-                    while (++$lastCell < $this->maxCols) {
-                        $moreCalls[] = array('tablecell_open', array(1, null, 1), $call[2]);
-                        $moreCalls[] = array('cdata', array(''), $call[2]);
-                        $moreCalls[] = array('tablecell_close', array(), $call[2]);
-                    }
-                    $moreCallsLength = count($moreCalls);
-                    if($moreCallsLength) {
-                        array_splice($this->tableCalls, $key, 0, $moreCalls);
-                        $key += $moreCallsLength;
-                    }
-
-                    if($this->countTableHeadRows == $lastRow) {
-                        array_splice($this->tableCalls, $key+1, 0, array(
-                              array('tablethead_close', array(), $call[2])));
-                    }
-                    break;
-
-            }
-        }
-
-        // condense cdata
-        $cnt = count($this->tableCalls);
-        for( $key = 0; $key < $cnt; $key++){
-            if($this->tableCalls[$key][0] == 'cdata'){
-                $ckey = $key;
-                $key++;
-                while($this->tableCalls[$key][0] == 'cdata'){
-                    $this->tableCalls[$ckey][1][0] .= $this->tableCalls[$key][1][0];
-                    $toDelete[] = $key;
-                    $key++;
-                }
-                continue;
-            }
-        }
-
-        foreach ( $toDelete as $delete ) {
-            unset($this->tableCalls[$delete]);
-        }
-        $this->tableCalls = array_values($this->tableCalls);
-    }
-}
-
-
-/**
- * Handler for paragraphs
- *
- * @author Harry Fuecks <hfuecks@gmail.com>
- */
-class Doku_Handler_Block {
-    var $calls = array();
-    var $skipEol = false;
-    var $inParagraph = false;
-
-    // Blocks these should not be inside paragraphs
-    var $blockOpen = array(
-            'header',
-            'listu_open','listo_open','listitem_open','listcontent_open',
-            'table_open','tablerow_open','tablecell_open','tableheader_open','tablethead_open',
-            'quote_open',
-            'code','file','hr','preformatted','rss',
-            'htmlblock','phpblock',
-            'footnote_open',
-        );
-
-    var $blockClose = array(
-            'header',
-            'listu_close','listo_close','listitem_close','listcontent_close',
-            'table_close','tablerow_close','tablecell_close','tableheader_close','tablethead_close',
-            'quote_close',
-            'code','file','hr','preformatted','rss',
-            'htmlblock','phpblock',
-            'footnote_close',
-        );
-
-    // Stacks can contain paragraphs
-    var $stackOpen = array(
-        'section_open',
-        );
-
-    var $stackClose = array(
-        'section_close',
-        );
-
-
-    /**
-     * Constructor. Adds loaded syntax plugins to the block and stack
-     * arrays
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function __construct(){
-        global $DOKU_PLUGINS;
-        //check if syntax plugins were loaded
-        if(empty($DOKU_PLUGINS['syntax'])) return;
-        foreach($DOKU_PLUGINS['syntax'] as $n => $p){
-            $ptype = $p->getPType();
-            if($ptype == 'block'){
-                $this->blockOpen[]  = 'plugin_'.$n;
-                $this->blockClose[] = 'plugin_'.$n;
-            }elseif($ptype == 'stack'){
-                $this->stackOpen[]  = 'plugin_'.$n;
-                $this->stackClose[] = 'plugin_'.$n;
-            }
-        }
-    }
-
-    function openParagraph($pos){
-        if ($this->inParagraph) return;
-        $this->calls[] = array('p_open',array(), $pos);
-        $this->inParagraph = true;
-        $this->skipEol = true;
-    }
-
-    /**
-     * Close a paragraph if needed
-     *
-     * This function makes sure there are no empty paragraphs on the stack
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     *
-     * @param string|integer $pos
-     */
-    function closeParagraph($pos){
-        if (!$this->inParagraph) return;
-        // look back if there was any content - we don't want empty paragraphs
-        $content = '';
-        $ccount = count($this->calls);
-        for($i=$ccount-1; $i>=0; $i--){
-            if($this->calls[$i][0] == 'p_open'){
-                break;
-            }elseif($this->calls[$i][0] == 'cdata'){
-                $content .= $this->calls[$i][1][0];
-            }else{
-                $content = 'found markup';
-                break;
-            }
-        }
-
-        if(trim($content)==''){
-            //remove the whole paragraph
-            //array_splice($this->calls,$i); // <- this is much slower than the loop below
-            for($x=$ccount; $x>$i; $x--) array_pop($this->calls);
-        }else{
-            // remove ending linebreaks in the paragraph
-            $i=count($this->calls)-1;
-            if ($this->calls[$i][0] == 'cdata') $this->calls[$i][1][0] = rtrim($this->calls[$i][1][0],DOKU_PARSER_EOL);
-            $this->calls[] = array('p_close',array(), $pos);
-        }
-
-        $this->inParagraph = false;
-        $this->skipEol = true;
-    }
-
-    function addCall($call) {
-        $key = count($this->calls);
-        if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
-            $this->calls[$key-1][1][0] .= $call[1][0];
-        } else {
-            $this->calls[] = $call;
-        }
-    }
-
-    // simple version of addCall, without checking cdata
-    function storeCall($call) {
-        $this->calls[] = $call;
-    }
-
-    /**
-     * Processes the whole instruction stack to open and close paragraphs
-     *
-     * @author Harry Fuecks <hfuecks@gmail.com>
-     * @author Andreas Gohr <andi@splitbrain.org>
-     *
-     * @param array $calls
-     *
-     * @return array
-     */
-    function process($calls) {
-        // open first paragraph
-        $this->openParagraph(0);
-        foreach ( $calls as $key => $call ) {
-            $cname = $call[0];
-            if ($cname == 'plugin') {
-                $cname='plugin_'.$call[1][0];
-                $plugin = true;
-                $plugin_open = (($call[1][2] == DOKU_LEXER_ENTER) || ($call[1][2] == DOKU_LEXER_SPECIAL));
-                $plugin_close = (($call[1][2] == DOKU_LEXER_EXIT) || ($call[1][2] == DOKU_LEXER_SPECIAL));
-            } else {
-                $plugin = false;
-            }
-            /* stack */
-            if ( in_array($cname,$this->stackClose ) && (!$plugin || $plugin_close)) {
-                $this->closeParagraph($call[2]);
-                $this->storeCall($call);
-                $this->openParagraph($call[2]);
-                continue;
-            }
-            if ( in_array($cname,$this->stackOpen ) && (!$plugin || $plugin_open) ) {
-                $this->closeParagraph($call[2]);
-                $this->storeCall($call);
-                $this->openParagraph($call[2]);
-                continue;
-            }
-            /* block */
-            // If it's a substition it opens and closes at the same call.
-            // To make sure next paragraph is correctly started, let close go first.
-            if ( in_array($cname, $this->blockClose) && (!$plugin || $plugin_close)) {
-                $this->closeParagraph($call[2]);
-                $this->storeCall($call);
-                $this->openParagraph($call[2]);
-                continue;
-            }
-            if ( in_array($cname, $this->blockOpen) && (!$plugin || $plugin_open)) {
-                $this->closeParagraph($call[2]);
-                $this->storeCall($call);
-                continue;
-            }
-            /* eol */
-            if ( $cname == 'eol' ) {
-                // Check this isn't an eol instruction to skip...
-                if ( !$this->skipEol ) {
-                    // Next is EOL => double eol => mark as paragraph
-                    if ( isset($calls[$key+1]) && $calls[$key+1][0] == 'eol' ) {
-                        $this->closeParagraph($call[2]);
-                        $this->openParagraph($call[2]);
-                    } else {
-                        //if this is just a single eol make a space from it
-                        $this->addCall(array('cdata',array(DOKU_PARSER_EOL), $call[2]));
-                    }
-                }
-                continue;
-            }
-            /* normal */
-            $this->addCall($call);
-            $this->skipEol = false;
-        }
-        // close last paragraph
-        $call = end($this->calls);
-        $this->closeParagraph($call[2]);
-        return $this->calls;
-    }
-}
-
-//Setup VIM: ex: et ts=4 :
diff --git a/wiki/inc/parser/lexer.php b/wiki/inc/parser/lexer.php
deleted file mode 100644
index ba6a653..0000000
--- a/wiki/inc/parser/lexer.php
+++ /dev/null
@@ -1,614 +0,0 @@
-<?php
-/**
- * Author Markus Baker: http://www.lastcraft.com
- * Version adapted from Simple Test: http://sourceforge.net/projects/simpletest/
- * For an intro to the Lexer see:
- * https://web.archive.org/web/20120125041816/http://www.phppatterns.com/docs/develop/simple_test_lexer_notes
- * @author Marcus Baker
- * @package Doku
- * @subpackage Lexer
- * @version $Id: lexer.php,v 1.1 2005/03/23 23:14:09 harryf Exp $
- */
-
-/**
- * Init path constant
- */
-if(!defined('DOKU_INC')) die('meh.');
-
-/**#@+
- * lexer mode constant
- */
-define("DOKU_LEXER_ENTER", 1);
-define("DOKU_LEXER_MATCHED", 2);
-define("DOKU_LEXER_UNMATCHED", 3);
-define("DOKU_LEXER_EXIT", 4);
-define("DOKU_LEXER_SPECIAL", 5);
-/**#@-*/
-
-/**
- * Compounded regular expression. Any of
- * the contained patterns could match and
- * when one does it's label is returned.
- *
- * @package Doku
- * @subpackage Lexer
- */
-class Doku_LexerParallelRegex {
-    var $_patterns;
-    var $_labels;
-    var $_regex;
-    var $_case;
-
-    /**
-     * Constructor. Starts with no patterns.
-     *
-     * @param boolean $case    True for case sensitive, false
-     *                         for insensitive.
-     * @access public
-     */
-    function __construct($case) {
-        $this->_case = $case;
-        $this->_patterns = array();
-        $this->_labels = array();
-        $this->_regex = null;
-    }
-
-    /**
-     * Adds a pattern with an optional label.
-     *
-     * @param mixed       $pattern Perl style regex. Must be UTF-8
-     *                             encoded. If its a string, the (, )
-     *                             lose their meaning unless they
-     *                             form part of a lookahead or
-     *                             lookbehind assertation.
-     * @param bool|string $label   Label of regex to be returned
-     *                             on a match. Label must be ASCII
-     * @access public
-     */
-    function addPattern($pattern, $label = true) {
-        $count = count($this->_patterns);
-        $this->_patterns[$count] = $pattern;
-        $this->_labels[$count] = $label;
-        $this->_regex = null;
-    }
-
-    /**
-     * Attempts to match all patterns at once against a string.
-     *
-     * @param string $subject      String to match against.
-     * @param string $match        First matched portion of
-     *                             subject.
-     * @return boolean             True on success.
-     * @access public
-     */
-    function match($subject, &$match) {
-        if (count($this->_patterns) == 0) {
-            return false;
-        }
-        if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) {
-            $match = "";
-            return false;
-        }
-
-        $match = $matches[0];
-        $size = count($matches);
-        for ($i = 1; $i < $size; $i++) {
-            if ($matches[$i] && isset($this->_labels[$i - 1])) {
-                return $this->_labels[$i - 1];
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Attempts to split the string against all patterns at once
-     *
-     * @param string $subject      String to match against.
-     * @param array $split         The split result: array containing, pre-match, match & post-match strings
-     * @return boolean             True on success.
-     * @access public
-     *
-     * @author Christopher Smith <chris@jalakai.co.uk>
-     */
-    function split($subject, &$split) {
-        if (count($this->_patterns) == 0) {
-            return false;
-        }
-
-        if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) {
-            if(function_exists('preg_last_error')){
-                $err = preg_last_error();
-                switch($err){
-                    case PREG_BACKTRACK_LIMIT_ERROR:
-                        msg('A PCRE backtrack error occured. Try to increase the pcre.backtrack_limit in php.ini',-1);
-                        break;
-                    case PREG_RECURSION_LIMIT_ERROR:
-                        msg('A PCRE recursion error occured. Try to increase the pcre.recursion_limit in php.ini',-1);
-                        break;
-                    case PREG_BAD_UTF8_ERROR:
-                        msg('A PCRE UTF-8 error occured. This might be caused by a faulty plugin',-1);
-                        break;
-                    case PREG_INTERNAL_ERROR:
-                        msg('A PCRE internal error occured. This might be caused by a faulty plugin',-1);
-                        break;
-                }
-            }
-
-            $split = array($subject, "", "");
-            return false;
-        }
-
-        $idx = count($matches)-2;
-        list($pre, $post) = preg_split($this->_patterns[$idx].$this->_getPerlMatchingFlags(), $subject, 2);
-        $split = array($pre, $matches[0], $post);
-
-        return isset($this->_labels[$idx]) ? $this->_labels[$idx] : true;
-    }
-
-    /**
-     * Compounds the patterns into a single
-     * regular expression separated with the
-     * "or" operator. Caches the regex.
-     * Will automatically escape (, ) and / tokens.
-     *
-     * @internal array $_patterns List of patterns in order.
-     * @return null|string
-     * @access private
-     */
-    function _getCompoundedRegex() {
-        if ($this->_regex == null) {
-            $cnt = count($this->_patterns);
-            for ($i = 0; $i < $cnt; $i++) {
-
-                /*
-                 * decompose the input pattern into "(", "(?", ")",
-                 * "[...]", "[]..]", "[^]..]", "[...[:...:]..]", "\x"...
-                 * elements.
-                 */
-                preg_match_all('/\\\\.|' .
-                               '\(\?|' .
-                               '[()]|' .
-                               '\[\^?\]?(?:\\\\.|\[:[^]]*:\]|[^]\\\\])*\]|' .
-                               '[^[()\\\\]+/', $this->_patterns[$i], $elts);
-
-                $pattern = "";
-                $level = 0;
-
-                foreach ($elts[0] as $elt) {
-                    /*
-                     * for "(", ")" remember the nesting level, add "\"
-                     * only to the non-"(?" ones.
-                     */
-
-                    switch($elt) {
-                        case '(':
-                            $pattern .= '\(';
-                            break;
-                        case ')':
-                            if ($level > 0)
-                                $level--; /* closing (? */
-                            else
-                                $pattern .= '\\';
-                            $pattern .= ')';
-                            break;
-                        case '(?':
-                            $level++;
-                            $pattern .= '(?';
-                            break;
-                        default:
-                            if (substr($elt, 0, 1) == '\\')
-                                $pattern .= $elt;
-                            else
-                                $pattern .= str_replace('/', '\/', $elt);
-                    }
-                }
-                $this->_patterns[$i] = "($pattern)";
-            }
-            $this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags();
-        }
-        return $this->_regex;
-    }
-
-    /**
-     * Accessor for perl regex mode flags to use.
-     * @return string       Perl regex flags.
-     * @access private
-     */
-    function _getPerlMatchingFlags() {
-        return ($this->_case ? "msS" : "msSi");
-    }
-}
-
-/**
- * States for a stack machine.
- * @package Lexer
- * @subpackage Lexer
- */
-class Doku_LexerStateStack {
-    var $_stack;
-
-    /**
-     * Constructor. Starts in named state.
-     * @param string $start        Starting state name.
-     * @access public
-     */
-    function __construct($start) {
-        $this->_stack = array($start);
-    }
-
-    /**
-     * Accessor for current state.
-     * @return string       State.
-     * @access public
-     */
-    function getCurrent() {
-        return $this->_stack[count($this->_stack) - 1];
-    }
-
-    /**
-     * Adds a state to the stack and sets it
-     * to be the current state.
-     * @param string $state        New state.
-     * @access public
-     */
-    function enter($state) {
-        array_push($this->_stack, $state);
-    }
-
-    /**
-     * Leaves the current state and reverts
-     * to the previous one.
-     * @return boolean    False if we drop off
-     *                    the bottom of the list.
-     * @access public
-     */
-    function leave() {
-        if (count($this->_stack) == 1) {
-            return false;
-        }
-        array_pop($this->_stack);
-        return true;
-    }
-}
-
-/**
- * Accepts text and breaks it into tokens.
- * Some optimisation to make the sure the
- * content is only scanned by the PHP regex
- * parser once. Lexer modes must not start
- * with leading underscores.
- * @package Doku
- * @subpackage Lexer
- */
-class Doku_Lexer {
-    var $_regexes;
-    var $_parser;
-    var $_mode;
-    var $_mode_handlers;
-    var $_case;
-
-    /**
-     * Sets up the lexer in case insensitive matching
-     * by default.
-     * @param Doku_Parser $parser  Handling strategy by
-     *                                 reference.
-     * @param string $start            Starting handler.
-     * @param boolean $case            True for case sensitive.
-     * @access public
-     */
-    function __construct($parser, $start = "accept", $case = false) {
-        $this->_case = $case;
-        /** @var Doku_LexerParallelRegex[] _regexes */
-        $this->_regexes = array();
-        $this->_parser = $parser;
-        $this->_mode = new Doku_LexerStateStack($start);
-        $this->_mode_handlers = array();
-    }
-
-    /**
-     * Adds a token search pattern for a particular
-     * parsing mode. The pattern does not change the
-     * current mode.
-     * @param string $pattern      Perl style regex, but ( and )
-     *                             lose the usual meaning.
-     * @param string $mode         Should only apply this
-     *                             pattern when dealing with
-     *                             this type of input.
-     * @access public
-     */
-    function addPattern($pattern, $mode = "accept") {
-        if (! isset($this->_regexes[$mode])) {
-            $this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
-        }
-        $this->_regexes[$mode]->addPattern($pattern);
-    }
-
-    /**
-     * Adds a pattern that will enter a new parsing
-     * mode. Useful for entering parenthesis, strings,
-     * tags, etc.
-     * @param string $pattern      Perl style regex, but ( and )
-     *                             lose the usual meaning.
-     * @param string $mode         Should only apply this
-     *                             pattern when dealing with
-     *                             this type of input.
-     * @param string $new_mode     Change parsing to this new
-     *                             nested mode.
-     * @access public
-     */
-    function addEntryPattern($pattern, $mode, $new_mode) {
-        if (! isset($this->_regexes[$mode])) {
-            $this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
-        }
-        $this->_regexes[$mode]->addPattern($pattern, $new_mode);
-    }
-
-    /**
-     * Adds a pattern that will exit the current mode
-     * and re-enter the previous one.
-     * @param string $pattern      Perl style regex, but ( and )
-     *                             lose the usual meaning.
-     * @param string $mode         Mode to leave.
-     * @access public
-     */
-    function addExitPattern($pattern, $mode) {
-        if (! isset($this->_regexes[$mode])) {
-            $this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
-        }
-        $this->_regexes[$mode]->addPattern($pattern, "__exit");
-    }
-
-    /**
-     * Adds a pattern that has a special mode. Acts as an entry
-     * and exit pattern in one go, effectively calling a special
-     * parser handler for this token only.
-     * @param string $pattern      Perl style regex, but ( and )
-     *                             lose the usual meaning.
-     * @param string $mode         Should only apply this
-     *                             pattern when dealing with
-     *                             this type of input.
-     * @param string $special      Use this mode for this one token.
-     * @access public
-     */
-    function addSpecialPattern($pattern, $mode, $special) {
-        if (! isset($this->_regexes[$mode])) {
-            $this->_regexes[$mode] = new Doku_LexerParallelRegex($this->_case);
-        }
-        $this->_regexes[$mode]->addPattern($pattern, "_$special");
-    }
-
-    /**
-     * Adds a mapping from a mode to another handler.
-     * @param string $mode        Mode to be remapped.
-     * @param string $handler     New target handler.
-     * @access public
-     */
-    function mapHandler($mode, $handler) {
-        $this->_mode_handlers[$mode] = $handler;
-    }
-
-    /**
-     * Splits the page text into tokens. Will fail
-     * if the handlers report an error or if no
-     * content is consumed. If successful then each
-     * unparsed and parsed token invokes a call to the
-     * held listener.
-     * @param string $raw        Raw HTML text.
-     * @return boolean           True on success, else false.
-     * @access public
-     */
-    function parse($raw) {
-        if (! isset($this->_parser)) {
-            return false;
-        }
-        $initialLength = strlen($raw);
-        $length = $initialLength;
-        $pos = 0;
-        while (is_array($parsed = $this->_reduce($raw))) {
-            list($unmatched, $matched, $mode) = $parsed;
-            $currentLength = strlen($raw);
-            $matchPos = $initialLength - $currentLength - strlen($matched);
-            if (! $this->_dispatchTokens($unmatched, $matched, $mode, $pos, $matchPos)) {
-                return false;
-            }
-            if ($currentLength == $length) {
-                return false;
-            }
-            $length = $currentLength;
-            $pos = $initialLength - $currentLength;
-        }
-        if (!$parsed) {
-            return false;
-        }
-        return $this->_invokeParser($raw, DOKU_LEXER_UNMATCHED, $pos);
-    }
-
-    /**
-     * Sends the matched token and any leading unmatched
-     * text to the parser changing the lexer to a new
-     * mode if one is listed.
-     * @param string $unmatched Unmatched leading portion.
-     * @param string $matched Actual token match.
-     * @param bool|string $mode Mode after match. A boolean
-     *                             false mode causes no change.
-     * @param int $initialPos
-     * @param int $matchPos
-     *                             Current byte index location in raw doc
-     *                             thats being parsed
-     * @return boolean             False if there was any error
-     *                             from the parser.
-     * @access private
-     */
-    function _dispatchTokens($unmatched, $matched, $mode = false, $initialPos, $matchPos) {
-        if (! $this->_invokeParser($unmatched, DOKU_LEXER_UNMATCHED, $initialPos) ){
-            return false;
-        }
-        if ($this->_isModeEnd($mode)) {
-            if (! $this->_invokeParser($matched, DOKU_LEXER_EXIT, $matchPos)) {
-                return false;
-            }
-            return $this->_mode->leave();
-        }
-        if ($this->_isSpecialMode($mode)) {
-            $this->_mode->enter($this->_decodeSpecial($mode));
-            if (! $this->_invokeParser($matched, DOKU_LEXER_SPECIAL, $matchPos)) {
-                return false;
-            }
-            return $this->_mode->leave();
-        }
-        if (is_string($mode)) {
-            $this->_mode->enter($mode);
-            return $this->_invokeParser($matched, DOKU_LEXER_ENTER, $matchPos);
-        }
-        return $this->_invokeParser($matched, DOKU_LEXER_MATCHED, $matchPos);
-    }
-
-    /**
-     * Tests to see if the new mode is actually to leave
-     * the current mode and pop an item from the matching
-     * mode stack.
-     * @param string $mode    Mode to test.
-     * @return boolean        True if this is the exit mode.
-     * @access private
-     */
-    function _isModeEnd($mode) {
-        return ($mode === "__exit");
-    }
-
-    /**
-     * Test to see if the mode is one where this mode
-     * is entered for this token only and automatically
-     * leaves immediately afterwoods.
-     * @param string $mode    Mode to test.
-     * @return boolean        True if this is the exit mode.
-     * @access private
-     */
-    function _isSpecialMode($mode) {
-        return (strncmp($mode, "_", 1) == 0);
-    }
-
-    /**
-     * Strips the magic underscore marking single token
-     * modes.
-     * @param string $mode    Mode to decode.
-     * @return string         Underlying mode name.
-     * @access private
-     */
-    function _decodeSpecial($mode) {
-        return substr($mode, 1);
-    }
-
-    /**
-     * Calls the parser method named after the current
-     * mode. Empty content will be ignored. The lexer
-     * has a parser handler for each mode in the lexer.
-     * @param string $content Text parsed.
-     * @param boolean $is_match Token is recognised rather
-     *                               than unparsed data.
-     * @param int $pos Current byte index location in raw doc
-     *                             thats being parsed
-     * @return bool
-     * @access private
-     */
-    function _invokeParser($content, $is_match, $pos) {
-        if (($content === "") || ($content === false)) {
-            return true;
-        }
-        $handler = $this->_mode->getCurrent();
-        if (isset($this->_mode_handlers[$handler])) {
-            $handler = $this->_mode_handlers[$handler];
-        }
-
-        // modes starting with plugin_ are all handled by the same
-        // handler but with an additional parameter
-        if(substr($handler,0,7)=='plugin_'){
-            list($handler,$plugin) = explode('_',$handler,2);
-            return $this->_parser->$handler($content, $is_match, $pos, $plugin);
-        }
-
-            return $this->_parser->$handler($content, $is_match, $pos);
-        }
-
-    /**
-     * Tries to match a chunk of text and if successful
-     * removes the recognised chunk and any leading
-     * unparsed data. Empty strings will not be matched.
-     * @param string $raw         The subject to parse. This is the
-     *                            content that will be eaten.
-     * @return array              Three item list of unparsed
-     *                            content followed by the
-     *                            recognised token and finally the
-     *                            action the parser is to take.
-     *                            True if no match, false if there
-     *                            is a parsing error.
-     * @access private
-     */
-    function _reduce(&$raw) {
-        if (! isset($this->_regexes[$this->_mode->getCurrent()])) {
-            return false;
-        }
-        if ($raw === "") {
-            return true;
-        }
-        if ($action = $this->_regexes[$this->_mode->getCurrent()]->split($raw, $split)) {
-            list($unparsed, $match, $raw) = $split;
-            return array($unparsed, $match, $action);
-        }
-        return true;
-    }
-}
-
-/**
- * Escapes regex characters other than (, ) and /
- *
- * @TODO
- *
- * @param string $str
- *
- * @return mixed
- */
-function Doku_Lexer_Escape($str) {
-    //$str = addslashes($str);
-    $chars = array(
-        '/\\\\/',
-        '/\./',
-        '/\+/',
-        '/\*/',
-        '/\?/',
-        '/\[/',
-        '/\^/',
-        '/\]/',
-        '/\$/',
-        '/\{/',
-        '/\}/',
-        '/\=/',
-        '/\!/',
-        '/\</',
-        '/\>/',
-        '/\|/',
-        '/\:/'
-        );
-
-    $escaped = array(
-        '\\\\\\\\',
-        '\.',
-        '\+',
-        '\*',
-        '\?',
-        '\[',
-        '\^',
-        '\]',
-        '\$',
-        '\{',
-        '\}',
-        '\=',
-        '\!',
-        '\<',
-        '\>',
-        '\|',
-        '\:'
-        );
-    return preg_replace($chars, $escaped, $str);
-}
-
-//Setup VIM: ex: et ts=4 sw=4 :
diff --git a/wiki/inc/parser/metadata.php b/wiki/inc/parser/metadata.php
deleted file mode 100644
index 9b1b5c9..0000000
--- a/wiki/inc/parser/metadata.php
+++ /dev/null
@@ -1,694 +0,0 @@
-<?php
-/**
- * Renderer for metadata
- *
- * @author Esther Brunner <wikidesign@gmail.com>
- */
-if(!defined('DOKU_INC')) die('meh.');
-
-if(!defined('DOKU_LF')) {
-    // Some whitespace to help View > Source
-    define ('DOKU_LF', "\n");
-}
-
-if(!defined('DOKU_TAB')) {
-    // Some whitespace to help View > Source
-    define ('DOKU_TAB', "\t");
-}
-
-/**
- * The MetaData Renderer
- *
- * Metadata is additional information about a DokuWiki page that gets extracted mainly from the page's content
- * but also it's own filesystem data (like the creation time). All metadata is stored in the fields $meta and
- * $persistent.
- *
- * Some simplified rendering to $doc is done to gather the page's (text-only) abstract.
- */
-class Doku_Renderer_metadata extends Doku_Renderer {
-    /** the approximate byte lenght to capture for the abstract */
-    const ABSTRACT_LEN = 250;
-
-    /** the maximum UTF8 character length for the abstract */
-    const ABSTRACT_MAX = 500;
-
-    /** @var array transient meta data, will be reset on each rendering */
-    public $meta = array();
-
-    /** @var array persistent meta data, will be kept until explicitly deleted */
-    public $persistent = array();
-
-    /** @var array the list of headers used to create unique link ids */
-    protected $headers = array();
-
-    /** @var string temporary $doc store */
-    protected $store = '';
-
-    /** @var string keeps the first image reference */
-    protected $firstimage = '';
-
-    /** @var bool determines if enough data for the abstract was collected, yet */
-    public $capture = true;
-
-    /** @var int number of bytes captured for abstract */
-    protected $captured = 0;
-
-    /**
-     * Returns the format produced by this renderer.
-     *
-     * @return string always 'metadata'
-     */
-    function getFormat() {
-        return 'metadata';
-    }
-
-    /**
-     * Initialize the document
-     *
-     * Sets up some of the persistent info about the page if it doesn't exist, yet.
-     */
-    function document_start() {
-        global $ID;
-
-        $this->headers = array();
-
-        // external pages are missing create date
-        if(!$this->persistent['date']['created']) {
-            $this->persistent['date']['created'] = filectime(wikiFN($ID));
-        }
-        if(!isset($this->persistent['user'])) {
-            $this->persistent['user'] = '';
-        }
-        if(!isset($this->persistent['creator'])) {
-            $this->persistent['creator'] = '';
-        }
-        // reset metadata to persistent values
-        $this->meta = $this->persistent;
-    }
-
-    /**
-     * Finalize the document
-     *
-     * Stores collected data in the metadata
-     */
-    function document_end() {
-        global $ID;
-
-        // store internal info in metadata (notoc,nocache)
-        $this->meta['internal'] = $this->info;
-
-        if(!isset($this->meta['description']['abstract'])) {
-            // cut off too long abstracts
-            $this->doc = trim($this->doc);
-            if(strlen($this->doc) > self::ABSTRACT_MAX) {
-                $this->doc = utf8_substr($this->doc, 0, self::ABSTRACT_MAX).'…';
-            }
-            $this->meta['description']['abstract'] = $this->doc;
-        }
-
-        $this->meta['relation']['firstimage'] = $this->firstimage;
-
-        if(!isset($this->meta['date']['modified'])) {
-            $this->meta['date']['modified'] = filemtime(wikiFN($ID));
-        }
-
-    }
-
-    /**
-     * Render plain text data
-     *
-     * This function takes care of the amount captured data and will stop capturing when
-     * enough abstract data is available
-     *
-     * @param $text
-     */
-    function cdata($text) {
-        if(!$this->capture) return;
-
-        $this->doc .= $text;
-
-        $this->captured += strlen($text);
-        if($this->captured > self::ABSTRACT_LEN) $this->capture = false;
-    }
-
-    /**
-     * Add an item to the TOC
-     *
-     * @param string $id       the hash link
-     * @param string $text     the text to display
-     * @param int    $level    the nesting level
-     */
-    function toc_additem($id, $text, $level) {
-        global $conf;
-
-        //only add items within configured levels
-        if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) {
-            // the TOC is one of our standard ul list arrays ;-)
-            $this->meta['description']['tableofcontents'][] = array(
-                'hid'   => $id,
-                'title' => $text,
-                'type'  => 'ul',
-                'level' => $level - $conf['toptoclevel'] + 1
-            );
-        }
-
-    }
-
-    /**
-     * Render a heading
-     *
-     * @param string $text  the text to display
-     * @param int    $level header level
-     * @param int    $pos   byte position in the original source
-     */
-    function header($text, $level, $pos) {
-        if(!isset($this->meta['title'])) $this->meta['title'] = $text;
-
-        // add the header to the TOC
-        $hid = $this->_headerToLink($text, true);
-        $this->toc_additem($hid, $text, $level);
-
-        // add to summary
-        $this->cdata(DOKU_LF.$text.DOKU_LF);
-    }
-
-    /**
-     * Open a paragraph
-     */
-    function p_open() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Close a paragraph
-     */
-    function p_close() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Create a line break
-     */
-    function linebreak() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Create a horizontal line
-     */
-    function hr() {
-        $this->cdata(DOKU_LF.'----------'.DOKU_LF);
-    }
-
-    /**
-     * Callback for footnote start syntax
-     *
-     * All following content will go to the footnote instead of
-     * the document. To achieve this the previous rendered content
-     * is moved to $store and $doc is cleared
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function footnote_open() {
-        if($this->capture) {
-            // move current content to store and record footnote
-            $this->store = $this->doc;
-            $this->doc   = '';
-        }
-    }
-
-    /**
-     * Callback for footnote end syntax
-     *
-     * All rendered content is moved to the $footnotes array and the old
-     * content is restored from $store again
-     *
-     * @author Andreas Gohr
-     */
-    function footnote_close() {
-        if($this->capture) {
-            // restore old content
-            $this->doc   = $this->store;
-            $this->store = '';
-        }
-    }
-
-    /**
-     * Open an unordered list
-     */
-    function listu_open() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Open an ordered list
-     */
-    function listo_open() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Open a list item
-     *
-     * @param int $level the nesting level
-     * @param bool $node true when a node; false when a leaf
-     */
-    function listitem_open($level,$node=false) {
-        $this->cdata(str_repeat(DOKU_TAB, $level).'* ');
-    }
-
-    /**
-     * Close a list item
-     */
-    function listitem_close() {
-        $this->cdata(DOKU_LF);
-    }
-
-    /**
-     * Output preformatted text
-     *
-     * @param string $text
-     */
-    function preformatted($text) {
-        $this->cdata($text);
-    }
-
-    /**
-     * Start a block quote
-     */
-    function quote_open() {
-        $this->cdata(DOKU_LF.DOKU_TAB.'"');
-    }
-
-    /**
-     * Stop a block quote
-     */
-    function quote_close() {
-        $this->cdata('"'.DOKU_LF);
-    }
-
-    /**
-     * Display text as file content, optionally syntax highlighted
-     *
-     * @param string $text text to show
-     * @param string $lang programming language to use for syntax highlighting
-     * @param string $file file path label
-     */
-    function file($text, $lang = null, $file = null) {
-        $this->cdata(DOKU_LF.$text.DOKU_LF);
-    }
-
-    /**
-     * Display text as code content, optionally syntax highlighted
-     *
-     * @param string $text     text to show
-     * @param string $language programming language to use for syntax highlighting
-     * @param string $file     file path label
-     */
-    function code($text, $language = null, $file = null) {
-        $this->cdata(DOKU_LF.$text.DOKU_LF);
-    }
-
-    /**
-     * Format an acronym
-     *
-     * Uses $this->acronyms
-     *
-     * @param string $acronym
-     */
-    function acronym($acronym) {
-        $this->cdata($acronym);
-    }
-
-    /**
-     * Format a smiley
-     *
-     * Uses $this->smiley
-     *
-     * @param string $smiley
-     */
-    function smiley($smiley) {
-        $this->cdata($smiley);
-    }
-
-    /**
-     * Format an entity
-     *
-     * Entities are basically small text replacements
-     *
-     * Uses $this->entities
-     *
-     * @param string $entity
-     */
-    function entity($entity) {
-        $this->cdata($entity);
-    }
-
-    /**
-     * Typographically format a multiply sign
-     *
-     * Example: ($x=640, $y=480) should result in "640×480"
-     *
-     * @param string|int $x first value
-     * @param string|int $y second value
-     */
-    function multiplyentity($x, $y) {
-        $this->cdata($x.'×'.$y);
-    }
-
-    /**
-     * Render an opening single quote char (language specific)
-     */
-    function singlequoteopening() {
-        global $lang;
-        $this->cdata($lang['singlequoteopening']);
-    }
-
-    /**
-     * Render a closing single quote char (language specific)
-     */
-    function singlequoteclosing() {
-        global $lang;
-        $this->cdata($lang['singlequoteclosing']);
-    }
-
-    /**
-     * Render an apostrophe char (language specific)
-     */
-    function apostrophe() {
-        global $lang;
-        $this->cdata($lang['apostrophe']);
-    }
-
-    /**
-     * Render an opening double quote char (language specific)
-     */
-    function doublequoteopening() {
-        global $lang;
-        $this->cdata($lang['doublequoteopening']);
-    }
-
-    /**
-     * Render an closinging double quote char (language specific)
-     */
-    function doublequoteclosing() {
-        global $lang;
-        $this->cdata($lang['doublequoteclosing']);
-    }
-
-    /**
-     * Render a CamelCase link
-     *
-     * @param string $link The link name
-     * @see http://en.wikipedia.org/wiki/CamelCase
-     */
-    function camelcaselink($link) {
-        $this->internallink($link, $link);
-    }
-
-    /**
-     * Render a page local link
-     *
-     * @param string $hash hash link identifier
-     * @param string $name name for the link
-     */
-    function locallink($hash, $name = null) {
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-    }
-
-    /**
-     * keep track of internal links in $this->meta['relation']['references']
-     *
-     * @param string            $id   page ID to link to. eg. 'wiki:syntax'
-     * @param string|array|null $name name for the link, array for media file
-     */
-    function internallink($id, $name = null) {
-        global $ID;
-
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-
-        $parts = explode('?', $id, 2);
-        if(count($parts) === 2) {
-            $id = $parts[0];
-        }
-
-        $default = $this->_simpleTitle($id);
-
-        // first resolve and clean up the $id
-        resolve_pageid(getNS($ID), $id, $exists);
-        @list($page) = explode('#', $id, 2);
-
-        // set metadata
-        $this->meta['relation']['references'][$page] = $exists;
-        // $data = array('relation' => array('isreferencedby' => array($ID => true)));
-        // p_set_metadata($id, $data);
-
-        // add link title to summary
-        if($this->capture) {
-            $name = $this->_getLinkTitle($name, $default, $id);
-            $this->doc .= $name;
-        }
-    }
-
-    /**
-     * Render an external link
-     *
-     * @param string            $url  full URL with scheme
-     * @param string|array|null $name name for the link, array for media file
-     */
-    function externallink($url, $name = null) {
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-
-        if($this->capture) {
-            $this->doc .= $this->_getLinkTitle($name, '<'.$url.'>');
-        }
-    }
-
-    /**
-     * Render an interwiki link
-     *
-     * You may want to use $this->_resolveInterWiki() here
-     *
-     * @param string       $match     original link - probably not much use
-     * @param string|array $name      name for the link, array for media file
-     * @param string       $wikiName  indentifier (shortcut) for the remote wiki
-     * @param string       $wikiUri   the fragment parsed from the original link
-     */
-    function interwikilink($match, $name = null, $wikiName, $wikiUri) {
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-
-        if($this->capture) {
-            list($wikiUri) = explode('#', $wikiUri, 2);
-            $name = $this->_getLinkTitle($name, $wikiUri);
-            $this->doc .= $name;
-        }
-    }
-
-    /**
-     * Link to windows share
-     *
-     * @param string       $url  the link
-     * @param string|array $name name for the link, array for media file
-     */
-    function windowssharelink($url, $name = null) {
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-
-        if($this->capture) {
-            if($name) $this->doc .= $name;
-            else $this->doc .= '<'.$url.'>';
-        }
-    }
-
-    /**
-     * Render a linked E-Mail Address
-     *
-     * Should honor $conf['mailguard'] setting
-     *
-     * @param string       $address Email-Address
-     * @param string|array $name    name for the link, array for media file
-     */
-    function emaillink($address, $name = null) {
-        if(is_array($name)) {
-            $this->_firstimage($name['src']);
-            if($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
-        }
-
-        if($this->capture) {
-            if($name) $this->doc .= $name;
-            else $this->doc .= '<'.$address.'>';
-        }
-    }
-
-    /**
-     * Render an internal media file
-     *
-     * @param string $src     media ID
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     * @param string $linking linkonly|detail|nolink
-     */
-    function internalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null) {
-        if($this->capture && $title) $this->doc .= '['.$title.']';
-        $this->_firstimage($src);
-        $this->_recordMediaUsage($src);
-    }
-
-    /**
-     * Render an external media file
-     *
-     * @param string $src     full media URL
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     * @param string $linking linkonly|detail|nolink
-     */
-    function externalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null) {
-        if($this->capture && $title) $this->doc .= '['.$title.']';
-        $this->_firstimage($src);
-    }
-
-    /**
-     * Render the output of an RSS feed
-     *
-     * @param string $url    URL of the feed
-     * @param array  $params Finetuning of the output
-     */
-    function rss($url, $params) {
-        $this->meta['relation']['haspart'][$url] = true;
-
-        $this->meta['date']['valid']['age'] =
-            isset($this->meta['date']['valid']['age']) ?
-                min($this->meta['date']['valid']['age'], $params['refresh']) :
-                $params['refresh'];
-    }
-
-    #region Utils
-
-    /**
-     * Removes any Namespace from the given name but keeps
-     * casing and special chars
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     *
-     * @param string $name
-     *
-     * @return mixed|string
-     */
-    function _simpleTitle($name) {
-        global $conf;
-
-        if(is_array($name)) return '';
-
-        if($conf['useslash']) {
-            $nssep = '[:;/]';
-        } else {
-            $nssep = '[:;]';
-        }
-        $name = preg_replace('!.*'.$nssep.'!', '', $name);
-        //if there is a hash we use the anchor name only
-        $name = preg_replace('!.*#!', '', $name);
-        return $name;
-    }
-
-    /**
-     * Creates a linkid from a headline
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param string  $title   The headline title
-     * @param boolean $create  Create a new unique ID?
-     * @return string
-     */
-    function _headerToLink($title, $create = false) {
-        if($create) {
-            return sectionID($title, $this->headers);
-        } else {
-            $check = false;
-            return sectionID($title, $check);
-        }
-    }
-
-    /**
-     * Construct a title and handle images in titles
-     *
-     * @author Harry Fuecks <hfuecks@gmail.com>
-     * @param string|array|null $title    either string title or media array
-     * @param string            $default  default title if nothing else is found
-     * @param null|string       $id       linked page id (used to extract title from first heading)
-     * @return string title text
-     */
-    function _getLinkTitle($title, $default, $id = null) {
-        if(is_array($title)) {
-            if($title['title']) {
-                return '['.$title['title'].']';
-            } else {
-                return $default;
-            }
-        } else if(is_null($title) || trim($title) == '') {
-            if(useHeading('content') && $id) {
-                $heading = p_get_first_heading($id, METADATA_DONT_RENDER);
-                if($heading) return $heading;
-            }
-            return $default;
-        } else {
-            return $title;
-        }
-    }
-
-    /**
-     * Remember first image
-     *
-     * @param string $src image URL or ID
-     */
-    function _firstimage($src) {
-        if($this->firstimage) return;
-        global $ID;
-
-        list($src) = explode('#', $src, 2);
-        if(!media_isexternal($src)) {
-            resolve_mediaid(getNS($ID), $src, $exists);
-        }
-        if(preg_match('/.(jpe?g|gif|png)$/i', $src)) {
-            $this->firstimage = $src;
-        }
-    }
-
-    /**
-     * Store list of used media files in metadata
-     *
-     * @param string $src media ID
-     */
-    function _recordMediaUsage($src) {
-        global $ID;
-
-        list ($src) = explode('#', $src, 2);
-        if(media_isexternal($src)) return;
-        resolve_mediaid(getNS($ID), $src, $exists);
-        $this->meta['relation']['media'][$src] = $exists;
-    }
-
-    #endregion
-}
-
-//Setup VIM: ex: et ts=4 :
diff --git a/wiki/inc/parser/parser.php b/wiki/inc/parser/parser.php
deleted file mode 100644
index 8cff2b8..0000000
--- a/wiki/inc/parser/parser.php
+++ /dev/null
@@ -1,1034 +0,0 @@
-<?php
-if(!defined('DOKU_INC')) die('meh.');
-require_once DOKU_INC . 'inc/parser/lexer.php';
-require_once DOKU_INC . 'inc/parser/handler.php';
-
-
-/**
- * Define various types of modes used by the parser - they are used to
- * populate the list of modes another mode accepts
- */
-global $PARSER_MODES;
-$PARSER_MODES = array(
-    // containers are complex modes that can contain many other modes
-    // hr breaks the principle but they shouldn't be used in tables / lists
-    // so they are put here
-    'container'    => array('listblock','table','quote','hr'),
-
-    // some mode are allowed inside the base mode only
-    'baseonly'     => array('header'),
-
-    // modes for styling text -- footnote behaves similar to styling
-    'formatting'   => array('strong', 'emphasis', 'underline', 'monospace',
-                            'subscript', 'superscript', 'deleted', 'footnote'),
-
-    // modes where the token is simply replaced - they can not contain any
-    // other modes
-    'substition'   => array('acronym','smiley','wordblock','entity',
-                            'camelcaselink', 'internallink','media',
-                            'externallink','linebreak','emaillink',
-                            'windowssharelink','filelink','notoc',
-                            'nocache','multiplyentity','quotes','rss'),
-
-    // modes which have a start and end token but inside which
-    // no other modes should be applied
-    'protected'    => array('preformatted','code','file','php','html','htmlblock','phpblock'),
-
-    // inside this mode no wiki markup should be applied but lineendings
-    // and whitespace isn't preserved
-    'disabled'     => array('unformatted'),
-
-    // used to mark paragraph boundaries
-    'paragraphs'   => array('eol')
-);
-
-//-------------------------------------------------------------------
-
-/**
- * Sets up the Lexer with modes and points it to the Handler
- * For an intro to the Lexer see: wiki:parser
- */
-class Doku_Parser {
-
-    var $Handler;
-
-    /**
-     * @var Doku_Lexer $Lexer
-     */
-    var $Lexer;
-
-    var $modes = array();
-
-    var $connected = false;
-
-    /**
-     * @param Doku_Parser_Mode_base $BaseMode
-     */
-    function addBaseMode($BaseMode) {
-        $this->modes['base'] = $BaseMode;
-        if ( !$this->Lexer ) {
-            $this->Lexer = new Doku_Lexer($this->Handler,'base', true);
-        }
-        $this->modes['base']->Lexer = $this->Lexer;
-    }
-
-    /**
-     * PHP preserves order of associative elements
-     * Mode sequence is important
-     *
-     * @param string $name
-     * @param Doku_Parser_Mode_Interface $Mode
-     */
-    function addMode($name, Doku_Parser_Mode_Interface $Mode) {
-        if ( !isset($this->modes['base']) ) {
-            $this->addBaseMode(new Doku_Parser_Mode_base());
-        }
-        $Mode->Lexer = $this->Lexer;
-        $this->modes[$name] = $Mode;
-    }
-
-    function connectModes() {
-
-        if ( $this->connected ) {
-            return;
-        }
-
-        foreach ( array_keys($this->modes) as $mode ) {
-
-            // Base isn't connected to anything
-            if ( $mode == 'base' ) {
-                continue;
-            }
-            $this->modes[$mode]->preConnect();
-
-            foreach ( array_keys($this->modes) as $cm ) {
-
-                if ( $this->modes[$cm]->accepts($mode) ) {
-                    $this->modes[$mode]->connectTo($cm);
-                }
-
-            }
-
-            $this->modes[$mode]->postConnect();
-        }
-
-        $this->connected = true;
-    }
-
-    function parse($doc) {
-        if ( $this->Lexer ) {
-            $this->connectModes();
-            // Normalize CRs and pad doc
-            $doc = "\n".str_replace("\r\n","\n",$doc)."\n";
-            $this->Lexer->parse($doc);
-            $this->Handler->_finalize();
-            return $this->Handler->calls;
-        } else {
-            return false;
-        }
-    }
-
-}
-
-//-------------------------------------------------------------------
-
-/**
- * Class Doku_Parser_Mode_Interface
- *
- * Defines a mode (syntax component) in the Parser
- */
-interface Doku_Parser_Mode_Interface {
-    /**
-     * returns a number used to determine in which order modes are added
-     */
-    public function getSort();
-
-    /**
-     * Called before any calls to connectTo
-     * @return void
-     */
-    function preConnect();
-
-    /**
-     * Connects the mode
-     *
-     * @param string $mode
-     * @return void
-     */
-    function connectTo($mode);
-
-    /**
-     * Called after all calls to connectTo
-     * @return void
-     */
-    function postConnect();
-
-    /**
-     * Check if given mode is accepted inside this mode
-     *
-     * @param string $mode
-     * @return bool
-     */
-    function accepts($mode);
-}
-
-/**
- * This class and all the subclasses below are used to reduce the effort required to register
- * modes with the Lexer.
- *
- * @author Harry Fuecks <hfuecks@gmail.com>
- */
-class Doku_Parser_Mode implements Doku_Parser_Mode_Interface {
-    /**
-     * @var Doku_Lexer $Lexer
-     */
-    var $Lexer;
-    var $allowedModes = array();
-
-    function getSort() {
-        trigger_error('getSort() not implemented in '.get_class($this), E_USER_WARNING);
-    }
-
-    function preConnect() {}
-    function connectTo($mode) {}
-    function postConnect() {}
-    function accepts($mode) {
-        return in_array($mode, (array) $this->allowedModes );
-    }
-}
-
-/**
- * Basically the same as Doku_Parser_Mode but extends from DokuWiki_Plugin
- *
- * Adds additional functions to syntax plugins
- */
-class Doku_Parser_Mode_Plugin extends DokuWiki_Plugin implements Doku_Parser_Mode_Interface {
-    /**
-     * @var Doku_Lexer $Lexer
-     */
-    var $Lexer;
-    var $allowedModes = array();
-
-    /**
-     * Sort for applying this mode
-     *
-     * @return int
-     */
-    function getSort() {
-        trigger_error('getSort() not implemented in '.get_class($this), E_USER_WARNING);
-    }
-
-    function preConnect() {}
-    function connectTo($mode) {}
-    function postConnect() {}
-    function accepts($mode) {
-        return in_array($mode, (array) $this->allowedModes );
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_base extends Doku_Parser_Mode {
-
-    function __construct() {
-        global $PARSER_MODES;
-
-        $this->allowedModes = array_merge (
-                $PARSER_MODES['container'],
-                $PARSER_MODES['baseonly'],
-                $PARSER_MODES['paragraphs'],
-                $PARSER_MODES['formatting'],
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['protected'],
-                $PARSER_MODES['disabled']
-            );
-    }
-
-    function getSort() {
-        return 0;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_footnote extends Doku_Parser_Mode {
-
-    function __construct() {
-        global $PARSER_MODES;
-
-        $this->allowedModes = array_merge (
-                $PARSER_MODES['container'],
-                $PARSER_MODES['formatting'],
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['protected'],
-                $PARSER_MODES['disabled']
-            );
-
-        unset($this->allowedModes[array_search('footnote', $this->allowedModes)]);
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern(
-            '\x28\x28(?=.*\x29\x29)',$mode,'footnote'
-            );
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern(
-            '\x29\x29','footnote'
-            );
-    }
-
-    function getSort() {
-        return 150;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_header extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        //we're not picky about the closing ones, two are enough
-        $this->Lexer->addSpecialPattern(
-                            '[ \t]*={2,}[^\n]+={2,}[ \t]*(?=\n)',
-                            $mode,
-                            'header'
-                        );
-    }
-
-    function getSort() {
-        return 50;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_notoc extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern('~~NOTOC~~',$mode,'notoc');
-    }
-
-    function getSort() {
-        return 30;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_nocache extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern('~~NOCACHE~~',$mode,'nocache');
-    }
-
-    function getSort() {
-        return 40;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_linebreak extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern('\x5C{2}(?:[ \t]|(?=\n))',$mode,'linebreak');
-    }
-
-    function getSort() {
-        return 140;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_eol extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $badModes = array('listblock','table');
-        if ( in_array($mode, $badModes) ) {
-            return;
-        }
-        // see FS#1652, pattern extended to swallow preceding whitespace to avoid issues with lines that only contain whitespace
-        $this->Lexer->addSpecialPattern('(?:^[ \t]*)?\n',$mode,'eol');
-    }
-
-    function getSort() {
-        return 370;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_hr extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern('\n[ \t]*-{4,}[ \t]*(?=\n)',$mode,'hr');
-    }
-
-    function getSort() {
-        return 160;
-    }
-}
-
-//-------------------------------------------------------------------
-/**
- * This class sets the markup for bold (=strong),
- * italic (=emphasis), underline etc.
- */
-class Doku_Parser_Mode_formatting extends Doku_Parser_Mode {
-    var $type;
-
-    var $formatting = array (
-        'strong' => array (
-            'entry'=>'\*\*(?=.*\*\*)',
-            'exit'=>'\*\*',
-            'sort'=>70
-            ),
-
-        'emphasis'=> array (
-            'entry'=>'//(?=[^\x00]*[^:])', //hack for bugs #384 #763 #1468
-            'exit'=>'//',
-            'sort'=>80
-            ),
-
-        'underline'=> array (
-            'entry'=>'__(?=.*__)',
-            'exit'=>'__',
-            'sort'=>90
-            ),
-
-        'monospace'=> array (
-            'entry'=>'\x27\x27(?=.*\x27\x27)',
-            'exit'=>'\x27\x27',
-            'sort'=>100
-            ),
-
-        'subscript'=> array (
-            'entry'=>'<sub>(?=.*</sub>)',
-            'exit'=>'</sub>',
-            'sort'=>110
-            ),
-
-        'superscript'=> array (
-            'entry'=>'<sup>(?=.*</sup>)',
-            'exit'=>'</sup>',
-            'sort'=>120
-            ),
-
-        'deleted'=> array (
-            'entry'=>'<del>(?=.*</del>)',
-            'exit'=>'</del>',
-            'sort'=>130
-            ),
-        );
-
-    /**
-     * @param string $type
-     */
-    function __construct($type) {
-        global $PARSER_MODES;
-
-        if ( !array_key_exists($type, $this->formatting) ) {
-            trigger_error('Invalid formatting type '.$type, E_USER_WARNING);
-        }
-
-        $this->type = $type;
-
-        // formatting may contain other formatting but not it self
-        $modes = $PARSER_MODES['formatting'];
-        $key = array_search($type, $modes);
-        if ( is_int($key) ) {
-            unset($modes[$key]);
-        }
-
-        $this->allowedModes = array_merge (
-                $modes,
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['disabled']
-            );
-    }
-
-    function connectTo($mode) {
-
-        // Can't nest formatting in itself
-        if ( $mode == $this->type ) {
-            return;
-        }
-
-        $this->Lexer->addEntryPattern(
-                $this->formatting[$this->type]['entry'],
-                $mode,
-                $this->type
-            );
-    }
-
-    function postConnect() {
-
-        $this->Lexer->addExitPattern(
-            $this->formatting[$this->type]['exit'],
-            $this->type
-            );
-
-    }
-
-    function getSort() {
-        return $this->formatting[$this->type]['sort'];
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_listblock extends Doku_Parser_Mode {
-
-    function __construct() {
-        global $PARSER_MODES;
-
-        $this->allowedModes = array_merge (
-                $PARSER_MODES['formatting'],
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['disabled'],
-                $PARSER_MODES['protected'] #XXX new
-            );
-
-    //    $this->allowedModes[] = 'footnote';
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('[ \t]*\n {2,}[\-\*]',$mode,'listblock');
-        $this->Lexer->addEntryPattern('[ \t]*\n\t{1,}[\-\*]',$mode,'listblock');
-
-        $this->Lexer->addPattern('\n {2,}[\-\*]','listblock');
-        $this->Lexer->addPattern('\n\t{1,}[\-\*]','listblock');
-
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('\n','listblock');
-    }
-
-    function getSort() {
-        return 10;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_table extends Doku_Parser_Mode {
-
-    function __construct() {
-        global $PARSER_MODES;
-
-        $this->allowedModes = array_merge (
-                $PARSER_MODES['formatting'],
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['disabled'],
-                $PARSER_MODES['protected']
-            );
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('[\t ]*\n\^',$mode,'table');
-        $this->Lexer->addEntryPattern('[\t ]*\n\|',$mode,'table');
-    }
-
-    function postConnect() {
-        $this->Lexer->addPattern('\n\^','table');
-        $this->Lexer->addPattern('\n\|','table');
-        $this->Lexer->addPattern('[\t ]*:::[\t ]*(?=[\|\^])','table');
-        $this->Lexer->addPattern('[\t ]+','table');
-        $this->Lexer->addPattern('\^','table');
-        $this->Lexer->addPattern('\|','table');
-        $this->Lexer->addExitPattern('\n','table');
-    }
-
-    function getSort() {
-        return 60;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_unformatted extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('<nowiki>(?=.*</nowiki>)',$mode,'unformatted');
-        $this->Lexer->addEntryPattern('%%(?=.*%%)',$mode,'unformattedalt');
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('</nowiki>','unformatted');
-        $this->Lexer->addExitPattern('%%','unformattedalt');
-        $this->Lexer->mapHandler('unformattedalt','unformatted');
-    }
-
-    function getSort() {
-        return 170;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_php extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('<php>(?=.*</php>)',$mode,'php');
-        $this->Lexer->addEntryPattern('<PHP>(?=.*</PHP>)',$mode,'phpblock');
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('</php>','php');
-        $this->Lexer->addExitPattern('</PHP>','phpblock');
-    }
-
-    function getSort() {
-        return 180;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_html extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('<html>(?=.*</html>)',$mode,'html');
-        $this->Lexer->addEntryPattern('<HTML>(?=.*</HTML>)',$mode,'htmlblock');
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('</html>','html');
-        $this->Lexer->addExitPattern('</HTML>','htmlblock');
-    }
-
-    function getSort() {
-        return 190;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_preformatted extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        // Has hard coded awareness of lists...
-        $this->Lexer->addEntryPattern('\n  (?![\*\-])',$mode,'preformatted');
-        $this->Lexer->addEntryPattern('\n\t(?![\*\-])',$mode,'preformatted');
-
-        // How to effect a sub pattern with the Lexer!
-        $this->Lexer->addPattern('\n  ','preformatted');
-        $this->Lexer->addPattern('\n\t','preformatted');
-
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('\n','preformatted');
-    }
-
-    function getSort() {
-        return 20;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_code extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('<code\b(?=.*</code>)',$mode,'code');
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('</code>','code');
-    }
-
-    function getSort() {
-        return 200;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_file extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('<file\b(?=.*</file>)',$mode,'file');
-    }
-
-    function postConnect() {
-        $this->Lexer->addExitPattern('</file>','file');
-    }
-
-    function getSort() {
-        return 210;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_quote extends Doku_Parser_Mode {
-
-    function __construct() {
-        global $PARSER_MODES;
-
-        $this->allowedModes = array_merge (
-                $PARSER_MODES['formatting'],
-                $PARSER_MODES['substition'],
-                $PARSER_MODES['disabled'],
-                $PARSER_MODES['protected'] #XXX new
-            );
-            #$this->allowedModes[] = 'footnote';
-            #$this->allowedModes[] = 'preformatted';
-            #$this->allowedModes[] = 'unformatted';
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addEntryPattern('\n>{1,}',$mode,'quote');
-    }
-
-    function postConnect() {
-        $this->Lexer->addPattern('\n>{1,}','quote');
-        $this->Lexer->addExitPattern('\n','quote');
-    }
-
-    function getSort() {
-        return 220;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_acronym extends Doku_Parser_Mode {
-    // A list
-    var $acronyms = array();
-    var $pattern = '';
-
-    function __construct($acronyms) {
-        usort($acronyms,array($this,'_compare'));
-        $this->acronyms = $acronyms;
-    }
-
-    function preConnect() {
-        if(!count($this->acronyms)) return;
-
-        $bound = '[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]';
-        $acronyms = array_map('Doku_Lexer_Escape',$this->acronyms);
-        $this->pattern = '(?<=^|'.$bound.')(?:'.join('|',$acronyms).')(?='.$bound.')';
-    }
-
-    function connectTo($mode) {
-        if(!count($this->acronyms)) return;
-
-        if ( strlen($this->pattern) > 0 ) {
-            $this->Lexer->addSpecialPattern($this->pattern,$mode,'acronym');
-        }
-    }
-
-    function getSort() {
-        return 240;
-    }
-
-    /**
-     * sort callback to order by string length descending
-     *
-     * @param string $a
-     * @param string $b
-     *
-     * @return int
-     */
-    function _compare($a,$b) {
-        $a_len = strlen($a);
-        $b_len = strlen($b);
-        if ($a_len > $b_len) {
-            return -1;
-        } else if ($a_len < $b_len) {
-            return 1;
-        }
-
-        return 0;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_smiley extends Doku_Parser_Mode {
-    // A list
-    var $smileys = array();
-    var $pattern = '';
-
-    function __construct($smileys) {
-        $this->smileys = $smileys;
-    }
-
-    function preConnect() {
-        if(!count($this->smileys) || $this->pattern != '') return;
-
-        $sep = '';
-        foreach ( $this->smileys as $smiley ) {
-            $this->pattern .= $sep.'(?<=\W|^)'.Doku_Lexer_Escape($smiley).'(?=\W|$)';
-            $sep = '|';
-        }
-    }
-
-    function connectTo($mode) {
-        if(!count($this->smileys)) return;
-
-        if ( strlen($this->pattern) > 0 ) {
-            $this->Lexer->addSpecialPattern($this->pattern,$mode,'smiley');
-        }
-    }
-
-    function getSort() {
-        return 230;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_wordblock extends Doku_Parser_Mode {
-    // A list
-    var $badwords = array();
-    var $pattern = '';
-
-    function __construct($badwords) {
-        $this->badwords = $badwords;
-    }
-
-    function preConnect() {
-
-        if ( count($this->badwords) == 0 || $this->pattern != '') {
-            return;
-        }
-
-        $sep = '';
-        foreach ( $this->badwords as $badword ) {
-            $this->pattern .= $sep.'(?<=\b)(?i)'.Doku_Lexer_Escape($badword).'(?-i)(?=\b)';
-            $sep = '|';
-        }
-
-    }
-
-    function connectTo($mode) {
-        if ( strlen($this->pattern) > 0 ) {
-            $this->Lexer->addSpecialPattern($this->pattern,$mode,'wordblock');
-        }
-    }
-
-    function getSort() {
-        return 250;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_entity extends Doku_Parser_Mode {
-    // A list
-    var $entities = array();
-    var $pattern = '';
-
-    function __construct($entities) {
-        $this->entities = $entities;
-    }
-
-    function preConnect() {
-        if(!count($this->entities) || $this->pattern != '') return;
-
-        $sep = '';
-        foreach ( $this->entities as $entity ) {
-            $this->pattern .= $sep.Doku_Lexer_Escape($entity);
-            $sep = '|';
-        }
-    }
-
-    function connectTo($mode) {
-        if(!count($this->entities)) return;
-
-        if ( strlen($this->pattern) > 0 ) {
-            $this->Lexer->addSpecialPattern($this->pattern,$mode,'entity');
-        }
-    }
-
-    function getSort() {
-        return 260;
-    }
-}
-
-//-------------------------------------------------------------------
-// Implements the 640x480 replacement
-class Doku_Parser_Mode_multiplyentity extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-
-        $this->Lexer->addSpecialPattern(
-                    '(?<=\b)(?:[1-9]|\d{2,})[xX]\d+(?=\b)',$mode,'multiplyentity'
-                );
-
-    }
-
-    function getSort() {
-        return 270;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_quotes extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        global $conf;
-
-        $ws   =  '\s/\#~:+=&%@\-\x28\x29\]\[{}><"\'';   // whitespace
-        $punc =  ';,\.?!';
-
-        if($conf['typography'] == 2){
-            $this->Lexer->addSpecialPattern(
-                        "(?<=^|[$ws])'(?=[^$ws$punc])",$mode,'singlequoteopening'
-                    );
-            $this->Lexer->addSpecialPattern(
-                        "(?<=^|[^$ws]|[$punc])'(?=$|[$ws$punc])",$mode,'singlequoteclosing'
-                    );
-            $this->Lexer->addSpecialPattern(
-                        "(?<=^|[^$ws$punc])'(?=$|[^$ws$punc])",$mode,'apostrophe'
-                    );
-        }
-
-        $this->Lexer->addSpecialPattern(
-                    "(?<=^|[$ws])\"(?=[^$ws$punc])",$mode,'doublequoteopening'
-                );
-        $this->Lexer->addSpecialPattern(
-                    "\"",$mode,'doublequoteclosing'
-                );
-
-    }
-
-    function getSort() {
-        return 280;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_camelcaselink extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern(
-                '\b[A-Z]+[a-z]+[A-Z][A-Za-z]*\b',$mode,'camelcaselink'
-            );
-    }
-
-    function getSort() {
-        return 290;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_internallink extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        // Word boundaries?
-        $this->Lexer->addSpecialPattern("\[\[.*?\]\](?!\])",$mode,'internallink');
-    }
-
-    function getSort() {
-        return 300;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_media extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        // Word boundaries?
-        $this->Lexer->addSpecialPattern("\{\{(?:[^\}]|(?:\}[^\}]))+\}\}",$mode,'media');
-    }
-
-    function getSort() {
-        return 320;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_rss extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern("\{\{rss>[^\}]+\}\}",$mode,'rss');
-    }
-
-    function getSort() {
-        return 310;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_externallink extends Doku_Parser_Mode {
-    var $schemes = array();
-    var $patterns = array();
-
-    function preConnect() {
-        if(count($this->patterns)) return;
-
-        $ltrs = '\w';
-        $gunk = '/\#~:.?+=&%@!\-\[\]';
-        $punc = '.:?\-;,';
-        $host = $ltrs.$punc;
-        $any  = $ltrs.$gunk.$punc;
-
-        $this->schemes = getSchemes();
-        foreach ( $this->schemes as $scheme ) {
-            $this->patterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
-        }
-
-        $this->patterns[] = '(?<=\s)(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
-        $this->patterns[] = '(?<=\s)(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
-    }
-
-    function connectTo($mode) {
-
-        foreach ( $this->patterns as $pattern ) {
-            $this->Lexer->addSpecialPattern($pattern,$mode,'externallink');
-        }
-    }
-
-    function getSort() {
-        return 330;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_filelink extends Doku_Parser_Mode {
-
-    var $pattern;
-
-    function preConnect() {
-
-        $ltrs = '\w';
-        $gunk = '/\#~:.?+=&%@!\-';
-        $punc = '.:?\-;,';
-        $host = $ltrs.$punc;
-        $any  = $ltrs.$gunk.$punc;
-
-        $this->pattern = '\b(?i)file(?-i)://['.$any.']+?['.
-            $punc.']*[^'.$any.']';
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern(
-            $this->pattern,$mode,'filelink');
-    }
-
-    function getSort() {
-        return 360;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_windowssharelink extends Doku_Parser_Mode {
-
-    var $pattern;
-
-    function preConnect() {
-        $this->pattern = "\\\\\\\\\w+?(?:\\\\[\w\-$]+)+";
-    }
-
-    function connectTo($mode) {
-        $this->Lexer->addSpecialPattern(
-            $this->pattern,$mode,'windowssharelink');
-    }
-
-    function getSort() {
-        return 350;
-    }
-}
-
-//-------------------------------------------------------------------
-class Doku_Parser_Mode_emaillink extends Doku_Parser_Mode {
-
-    function connectTo($mode) {
-        // pattern below is defined in inc/mail.php
-        $this->Lexer->addSpecialPattern('<'.PREG_PATTERN_VALID_EMAIL.'>',$mode,'emaillink');
-    }
-
-    function getSort() {
-        return 340;
-    }
-}
-
-
-//Setup VIM: ex: et ts=4 :
diff --git a/wiki/inc/parser/renderer.php b/wiki/inc/parser/renderer.php
deleted file mode 100644
index 83b51d4..0000000
--- a/wiki/inc/parser/renderer.php
+++ /dev/null
@@ -1,883 +0,0 @@
-<?php
-/**
- * Renderer output base class
- *
- * @author Harry Fuecks <hfuecks@gmail.com>
- * @author Andreas Gohr <andi@splitbrain.org>
- */
-if(!defined('DOKU_INC')) die('meh.');
-
-/**
- * Allowed chars in $language for code highlighting
- * @see GeSHi::set_language()
- */
-define('PREG_PATTERN_VALID_LANGUAGE', '#[^a-zA-Z0-9\-_]#');
-
-/**
- * An empty renderer, produces no output
- *
- * Inherits from DokuWiki_Plugin for giving additional functions to render plugins
- *
- * The renderer transforms the syntax instructions created by the parser and handler into the
- * desired output format. For each instruction a corresponding method defined in this class will
- * be called. That method needs to produce the desired output for the instruction and add it to the
- * $doc field. When all instructions are processed, the $doc field contents will be cached by
- * DokuWiki and sent to the user.
- */
-class Doku_Renderer extends DokuWiki_Plugin {
-    /** @var array Settings, control the behavior of the renderer */
-    public $info = array(
-        'cache' => true, // may the rendered result cached?
-        'toc'   => true, // render the TOC?
-    );
-
-    /** @var array contains the smiley configuration, set in p_render() */
-    public $smileys = array();
-    /** @var array contains the entity configuration, set in p_render() */
-    public $entities = array();
-    /** @var array contains the acronym configuration, set in p_render() */
-    public $acronyms = array();
-    /** @var array contains the interwiki configuration, set in p_render() */
-    public $interwiki = array();
-
-    /**
-     * @var string the rendered document, this will be cached after the renderer ran through
-     */
-    public $doc = '';
-
-    /**
-     * clean out any per-use values
-     *
-     * This is called before each use of the renderer object and should be used to
-     * completely reset the state of the renderer to be reused for a new document
-     */
-    function reset() {
-    }
-
-    /**
-     * Allow the plugin to prevent DokuWiki from reusing an instance
-     *
-     * Since most renderer plugins fail to implement Doku_Renderer::reset() we default
-     * to reinstantiating the renderer here
-     *
-     * @return bool   false if the plugin has to be instantiated
-     */
-    function isSingleton() {
-        return false;
-    }
-
-    /**
-     * Returns the format produced by this renderer.
-     *
-     * Has to be overidden by sub classes
-     *
-     * @return string
-     */
-    function getFormat() {
-        trigger_error('getFormat() not implemented in '.get_class($this), E_USER_WARNING);
-        return '';
-    }
-
-    /**
-     * Disable caching of this renderer's output
-     */
-    function nocache() {
-        $this->info['cache'] = false;
-    }
-
-    /**
-     * Disable TOC generation for this renderer's output
-     *
-     * This might not be used for certain sub renderer
-     */
-    function notoc() {
-        $this->info['toc'] = false;
-    }
-
-    /**
-     * Handle plugin rendering
-     *
-     * Most likely this needs NOT to be overwritten by sub classes
-     *
-     * @param string $name  Plugin name
-     * @param mixed  $data  custom data set by handler
-     * @param string $state matched state if any
-     * @param string $match raw matched syntax
-     */
-    function plugin($name, $data, $state = '', $match = '') {
-        /** @var DokuWiki_Syntax_Plugin $plugin */
-        $plugin = plugin_load('syntax', $name);
-        if($plugin != null) {
-            $plugin->render($this->getFormat(), $this, $data);
-        }
-    }
-
-    /**
-     * handle nested render instructions
-     * this method (and nest_close method) should not be overloaded in actual renderer output classes
-     *
-     * @param array $instructions
-     */
-    function nest($instructions) {
-        foreach($instructions as $instruction) {
-            // execute the callback against ourself
-            if(method_exists($this, $instruction[0])) {
-                call_user_func_array(array($this, $instruction[0]), $instruction[1] ? $instruction[1] : array());
-            }
-        }
-    }
-
-    /**
-     * dummy closing instruction issued by Doku_Handler_Nest
-     *
-     * normally the syntax mode should override this instruction when instantiating Doku_Handler_Nest -
-     * however plugins will not be able to - as their instructions require data.
-     */
-    function nest_close() {
-    }
-
-    #region Syntax modes - sub classes will need to implement them to fill $doc
-
-    /**
-     * Initialize the document
-     */
-    function document_start() {
-    }
-
-    /**
-     * Finalize the document
-     */
-    function document_end() {
-    }
-
-    /**
-     * Render the Table of Contents
-     *
-     * @return string
-     */
-    function render_TOC() {
-        return '';
-    }
-
-    /**
-     * Add an item to the TOC
-     *
-     * @param string $id       the hash link
-     * @param string $text     the text to display
-     * @param int    $level    the nesting level
-     */
-    function toc_additem($id, $text, $level) {
-    }
-
-    /**
-     * Render a heading
-     *
-     * @param string $text  the text to display
-     * @param int    $level header level
-     * @param int    $pos   byte position in the original source
-     */
-    function header($text, $level, $pos) {
-    }
-
-    /**
-     * Open a new section
-     *
-     * @param int $level section level (as determined by the previous header)
-     */
-    function section_open($level) {
-    }
-
-    /**
-     * Close the current section
-     */
-    function section_close() {
-    }
-
-    /**
-     * Render plain text data
-     *
-     * @param string $text
-     */
-    function cdata($text) {
-    }
-
-    /**
-     * Open a paragraph
-     */
-    function p_open() {
-    }
-
-    /**
-     * Close a paragraph
-     */
-    function p_close() {
-    }
-
-    /**
-     * Create a line break
-     */
-    function linebreak() {
-    }
-
-    /**
-     * Create a horizontal line
-     */
-    function hr() {
-    }
-
-    /**
-     * Start strong (bold) formatting
-     */
-    function strong_open() {
-    }
-
-    /**
-     * Stop strong (bold) formatting
-     */
-    function strong_close() {
-    }
-
-    /**
-     * Start emphasis (italics) formatting
-     */
-    function emphasis_open() {
-    }
-
-    /**
-     * Stop emphasis (italics) formatting
-     */
-    function emphasis_close() {
-    }
-
-    /**
-     * Start underline formatting
-     */
-    function underline_open() {
-    }
-
-    /**
-     * Stop underline formatting
-     */
-    function underline_close() {
-    }
-
-    /**
-     * Start monospace formatting
-     */
-    function monospace_open() {
-    }
-
-    /**
-     * Stop monospace formatting
-     */
-    function monospace_close() {
-    }
-
-    /**
-     * Start a subscript
-     */
-    function subscript_open() {
-    }
-
-    /**
-     * Stop a subscript
-     */
-    function subscript_close() {
-    }
-
-    /**
-     * Start a superscript
-     */
-    function superscript_open() {
-    }
-
-    /**
-     * Stop a superscript
-     */
-    function superscript_close() {
-    }
-
-    /**
-     * Start deleted (strike-through) formatting
-     */
-    function deleted_open() {
-    }
-
-    /**
-     * Stop deleted (strike-through) formatting
-     */
-    function deleted_close() {
-    }
-
-    /**
-     * Start a footnote
-     */
-    function footnote_open() {
-    }
-
-    /**
-     * Stop a footnote
-     */
-    function footnote_close() {
-    }
-
-    /**
-     * Open an unordered list
-     */
-    function listu_open() {
-    }
-
-    /**
-     * Close an unordered list
-     */
-    function listu_close() {
-    }
-
-    /**
-     * Open an ordered list
-     */
-    function listo_open() {
-    }
-
-    /**
-     * Close an ordered list
-     */
-    function listo_close() {
-    }
-
-    /**
-     * Open a list item
-     *
-     * @param int $level the nesting level
-     * @param bool $node true when a node; false when a leaf
-     */
-    function listitem_open($level,$node=false) {
-    }
-
-    /**
-     * Close a list item
-     */
-    function listitem_close() {
-    }
-
-    /**
-     * Start the content of a list item
-     */
-    function listcontent_open() {
-    }
-
-    /**
-     * Stop the content of a list item
-     */
-    function listcontent_close() {
-    }
-
-    /**
-     * Output unformatted $text
-     *
-     * Defaults to $this->cdata()
-     *
-     * @param string $text
-     */
-    function unformatted($text) {
-        $this->cdata($text);
-    }
-
-    /**
-     * Output inline PHP code
-     *
-     * If $conf['phpok'] is true this should evaluate the given code and append the result
-     * to $doc
-     *
-     * @param string $text The PHP code
-     */
-    function php($text) {
-    }
-
-    /**
-     * Output block level PHP code
-     *
-     * If $conf['phpok'] is true this should evaluate the given code and append the result
-     * to $doc
-     *
-     * @param string $text The PHP code
-     */
-    function phpblock($text) {
-    }
-
-    /**
-     * Output raw inline HTML
-     *
-     * If $conf['htmlok'] is true this should add the code as is to $doc
-     *
-     * @param string $text The HTML
-     */
-    function html($text) {
-    }
-
-    /**
-     * Output raw block-level HTML
-     *
-     * If $conf['htmlok'] is true this should add the code as is to $doc
-     *
-     * @param string $text The HTML
-     */
-    function htmlblock($text) {
-    }
-
-    /**
-     * Output preformatted text
-     *
-     * @param string $text
-     */
-    function preformatted($text) {
-    }
-
-    /**
-     * Start a block quote
-     */
-    function quote_open() {
-    }
-
-    /**
-     * Stop a block quote
-     */
-    function quote_close() {
-    }
-
-    /**
-     * Display text as file content, optionally syntax highlighted
-     *
-     * @param string $text text to show
-     * @param string $lang programming language to use for syntax highlighting
-     * @param string $file file path label
-     */
-    function file($text, $lang = null, $file = null) {
-    }
-
-    /**
-     * Display text as code content, optionally syntax highlighted
-     *
-     * @param string $text text to show
-     * @param string $lang programming language to use for syntax highlighting
-     * @param string $file file path label
-     */
-    function code($text, $lang = null, $file = null) {
-    }
-
-    /**
-     * Format an acronym
-     *
-     * Uses $this->acronyms
-     *
-     * @param string $acronym
-     */
-    function acronym($acronym) {
-    }
-
-    /**
-     * Format a smiley
-     *
-     * Uses $this->smiley
-     *
-     * @param string $smiley
-     */
-    function smiley($smiley) {
-    }
-
-    /**
-     * Format an entity
-     *
-     * Entities are basically small text replacements
-     *
-     * Uses $this->entities
-     *
-     * @param string $entity
-     */
-    function entity($entity) {
-    }
-
-    /**
-     * Typographically format a multiply sign
-     *
-     * Example: ($x=640, $y=480) should result in "640×480"
-     *
-     * @param string|int $x first value
-     * @param string|int $y second value
-     */
-    function multiplyentity($x, $y) {
-    }
-
-    /**
-     * Render an opening single quote char (language specific)
-     */
-    function singlequoteopening() {
-    }
-
-    /**
-     * Render a closing single quote char (language specific)
-     */
-    function singlequoteclosing() {
-    }
-
-    /**
-     * Render an apostrophe char (language specific)
-     */
-    function apostrophe() {
-    }
-
-    /**
-     * Render an opening double quote char (language specific)
-     */
-    function doublequoteopening() {
-    }
-
-    /**
-     * Render an closinging double quote char (language specific)
-     */
-    function doublequoteclosing() {
-    }
-
-    /**
-     * Render a CamelCase link
-     *
-     * @param string $link The link name
-     * @see http://en.wikipedia.org/wiki/CamelCase
-     */
-    function camelcaselink($link) {
-    }
-
-    /**
-     * Render a page local link
-     *
-     * @param string $hash hash link identifier
-     * @param string $name name for the link
-     */
-    function locallink($hash, $name = null) {
-    }
-
-    /**
-     * Render a wiki internal link
-     *
-     * @param string       $link  page ID to link to. eg. 'wiki:syntax'
-     * @param string|array $title name for the link, array for media file
-     */
-    function internallink($link, $title = null) {
-    }
-
-    /**
-     * Render an external link
-     *
-     * @param string       $link  full URL with scheme
-     * @param string|array $title name for the link, array for media file
-     */
-    function externallink($link, $title = null) {
-    }
-
-    /**
-     * Render the output of an RSS feed
-     *
-     * @param string $url    URL of the feed
-     * @param array  $params Finetuning of the output
-     */
-    function rss($url, $params) {
-    }
-
-    /**
-     * Render an interwiki link
-     *
-     * You may want to use $this->_resolveInterWiki() here
-     *
-     * @param string       $link     original link - probably not much use
-     * @param string|array $title    name for the link, array for media file
-     * @param string       $wikiName indentifier (shortcut) for the remote wiki
-     * @param string       $wikiUri  the fragment parsed from the original link
-     */
-    function interwikilink($link, $title = null, $wikiName, $wikiUri) {
-    }
-
-    /**
-     * Link to file on users OS
-     *
-     * @param string       $link  the link
-     * @param string|array $title name for the link, array for media file
-     */
-    function filelink($link, $title = null) {
-    }
-
-    /**
-     * Link to windows share
-     *
-     * @param string       $link  the link
-     * @param string|array $title name for the link, array for media file
-     */
-    function windowssharelink($link, $title = null) {
-    }
-
-    /**
-     * Render a linked E-Mail Address
-     *
-     * Should honor $conf['mailguard'] setting
-     *
-     * @param string $address Email-Address
-     * @param string|array $name name for the link, array for media file
-     */
-    function emaillink($address, $name = null) {
-    }
-
-    /**
-     * Render an internal media file
-     *
-     * @param string $src     media ID
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     * @param string $linking linkonly|detail|nolink
-     */
-    function internalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null) {
-    }
-
-    /**
-     * Render an external media file
-     *
-     * @param string $src     full media URL
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     * @param string $linking linkonly|detail|nolink
-     */
-    function externalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null) {
-    }
-
-    /**
-     * Render a link to an internal media file
-     *
-     * @param string $src     media ID
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     */
-    function internalmedialink($src, $title = null, $align = null,
-                               $width = null, $height = null, $cache = null) {
-    }
-
-    /**
-     * Render a link to an external media file
-     *
-     * @param string $src     media ID
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     */
-    function externalmedialink($src, $title = null, $align = null,
-                               $width = null, $height = null, $cache = null) {
-    }
-
-    /**
-     * Start a table
-     *
-     * @param int $maxcols maximum number of columns
-     * @param int $numrows NOT IMPLEMENTED
-     * @param int $pos     byte position in the original source
-     */
-    function table_open($maxcols = null, $numrows = null, $pos = null) {
-    }
-
-    /**
-     * Close a table
-     *
-     * @param int $pos byte position in the original source
-     */
-    function table_close($pos = null) {
-    }
-
-    /**
-     * Open a table header
-     */
-    function tablethead_open() {
-    }
-
-    /**
-     * Close a table header
-     */
-    function tablethead_close() {
-    }
-
-    /**
-     * Open a table body
-     */
-    function tabletbody_open() {
-    }
-
-    /**
-     * Close a table body
-     */
-    function tabletbody_close() {
-    }
-
-    /**
-     * Open a table footer
-     */
-    function tabletfoot_open() {
-    }
-
-    /**
-     * Close a table footer
-     */
-    function tabletfoot_close() {
-    }
-
-    /**
-     * Open a table row
-     */
-    function tablerow_open() {
-    }
-
-    /**
-     * Close a table row
-     */
-    function tablerow_close() {
-    }
-
-    /**
-     * Open a table header cell
-     *
-     * @param int    $colspan
-     * @param string $align left|center|right
-     * @param int    $rowspan
-     */
-    function tableheader_open($colspan = 1, $align = null, $rowspan = 1) {
-    }
-
-    /**
-     * Close a table header cell
-     */
-    function tableheader_close() {
-    }
-
-    /**
-     * Open a table cell
-     *
-     * @param int    $colspan
-     * @param string $align left|center|right
-     * @param int    $rowspan
-     */
-    function tablecell_open($colspan = 1, $align = null, $rowspan = 1) {
-    }
-
-    /**
-     * Close a table cell
-     */
-    function tablecell_close() {
-    }
-
-    #endregion
-
-    #region util functions, you probably won't need to reimplement them
-
-    /**
-     * Removes any Namespace from the given name but keeps
-     * casing and special chars
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     *
-     * @param string $name
-     * @return string
-     */
-    function _simpleTitle($name) {
-        global $conf;
-
-        //if there is a hash we use the ancor name only
-        @list($name, $hash) = explode('#', $name, 2);
-        if($hash) return $hash;
-
-        if($conf['useslash']) {
-            $name = strtr($name, ';/', ';:');
-        } else {
-            $name = strtr($name, ';', ':');
-        }
-
-        return noNSorNS($name);
-    }
-
-    /**
-     * Resolve an interwikilink
-     *
-     * @param string    $shortcut  identifier for the interwiki link
-     * @param string    $reference fragment that refers the content
-     * @param null|bool $exists    reference which returns if an internal page exists
-     * @return string interwikilink
-     */
-    function _resolveInterWiki(&$shortcut, $reference, &$exists = null) {
-        //get interwiki URL
-        if(isset($this->interwiki[$shortcut])) {
-            $url = $this->interwiki[$shortcut];
-        } else {
-            // Default to Google I'm feeling lucky
-            $url      = 'https://www.google.com/search?q={URL}&amp;btnI=lucky';
-            $shortcut = 'go';
-        }
-
-        //split into hash and url part
-        $hash = strrchr($reference, '#');
-        if($hash) {
-            $reference = substr($reference, 0, -strlen($hash));
-            $hash = substr($hash, 1);
-        }
-
-        //replace placeholder
-        if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) {
-            //use placeholders
-            $url    = str_replace('{URL}', rawurlencode($reference), $url);
-            //wiki names will be cleaned next, otherwise urlencode unsafe chars
-            $url    = str_replace('{NAME}', ($url{0} === ':') ? $reference :
-                                  preg_replace_callback('/[[\\\\\]^`{|}#%]/', function($match) {
-                                    return rawurlencode($match[0]);
-                                  }, $reference), $url);
-            $parsed = parse_url($reference);
-            if (empty($parsed['scheme'])) $parsed['scheme'] = '';
-            if (empty($parsed['host'])) $parsed['host'] = '';
-            if (empty($parsed['port'])) $parsed['port'] = 80;
-            if (empty($parsed['path'])) $parsed['path'] = '';
-            if (empty($parsed['query'])) $parsed['query'] = '';
-            $url = strtr($url,[
-                '{SCHEME}' => $parsed['scheme'],
-                '{HOST}' => $parsed['host'],
-                '{PORT}' => $parsed['port'],
-                '{PATH}' => $parsed['path'],
-                '{QUERY}' => $parsed['query'] ,
-            ]);
-        } else {
-            //default
-            $url = $url.rawurlencode($reference);
-        }
-        //handle as wiki links
-        if($url{0} === ':') {
-            $urlparam = null;
-            $id = $url;
-            if (strpos($url, '?') !== false) {
-                list($id, $urlparam) = explode('?', $url, 2);
-            }
-            $url    = wl(cleanID($id), $urlparam);
-            $exists = page_exists($id);
-        }
-        if($hash) $url .= '#'.rawurlencode($hash);
-
-        return $url;
-    }
-
-    #endregion
-}
-
-
-//Setup VIM: ex: et ts=4 :
diff --git a/wiki/inc/parser/xhtml.php b/wiki/inc/parser/xhtml.php
deleted file mode 100644
index a3e7b45..0000000
--- a/wiki/inc/parser/xhtml.php
+++ /dev/null
@@ -1,1970 +0,0 @@
-<?php
-/**
- * Renderer for XHTML output
- *
- * @author Harry Fuecks <hfuecks@gmail.com>
- * @author Andreas Gohr <andi@splitbrain.org>
- */
-if(!defined('DOKU_INC')) die('meh.');
-
-if(!defined('DOKU_LF')) {
-    // Some whitespace to help View > Source
-    define ('DOKU_LF', "\n");
-}
-
-if(!defined('DOKU_TAB')) {
-    // Some whitespace to help View > Source
-    define ('DOKU_TAB', "\t");
-}
-
-/**
- * The XHTML Renderer
- *
- * This is DokuWiki's main renderer used to display page content in the wiki
- */
-class Doku_Renderer_xhtml extends Doku_Renderer {
-    /** @var array store the table of contents */
-    public $toc = array();
-
-    /** @var array A stack of section edit data */
-    protected $sectionedits = array();
-    var $date_at = '';    // link pages and media against this revision
-
-    /** @var int last section edit id, used by startSectionEdit */
-    protected $lastsecid = 0;
-
-    /** @var array the list of headers used to create unique link ids */
-    protected $headers = array();
-
-    /** @var array a list of footnotes, list starts at 1! */
-    protected $footnotes = array();
-
-    /** @var int current section level */
-    protected $lastlevel = 0;
-    /** @var array section node tracker */
-    protected $node = array(0, 0, 0, 0, 0);
-
-    /** @var string temporary $doc store */
-    protected $store = '';
-
-    /** @var array global counter, for table classes etc. */
-    protected $_counter = array(); //
-
-    /** @var int counts the code and file blocks, used to provide download links */
-    protected $_codeblock = 0;
-
-    /** @var array list of allowed URL schemes */
-    protected $schemes = null;
-
-    /**
-     * Register a new edit section range
-     *
-     * @param int    $start  The byte position for the edit start
-     * @param array  $data   Associative array with section data:
-     *                       Key 'name': the section name/title
-     *                       Key 'target': the target for the section edit,
-     *                                     e.g. 'section' or 'table'
-     *                       Key 'hid': header id
-     *                       Key 'codeblockOffset': actual code block index
-     *                       Key 'start': set in startSectionEdit(),
-     *                                    do not set yourself
-     *                       Key 'range': calculated from 'start' and
-     *                                    $key in finishSectionEdit(),
-     *                                    do not set yourself
-     * @return string  A marker class for the starting HTML element
-     *
-     * @author Adrian Lang <lang@cosmocode.de>
-     */
-    public function startSectionEdit($start, $data) {
-        if (!is_array($data)) {
-            msg(
-                sprintf(
-                    'startSectionEdit: $data "%s" is NOT an array! One of your plugins needs an update.',
-                    hsc((string) $data)
-                ), -1
-            );
-
-            // @deprecated 2018-04-14, backward compatibility
-            $args = func_get_args();
-            $data = array();
-            if(isset($args[1])) $data['target'] = $args[1];
-            if(isset($args[2])) $data['name'] = $args[2];
-            if(isset($args[3])) $data['hid'] = $args[3];
-        }
-        $data['secid'] = ++$this->lastsecid;
-        $data['start'] = $start;
-        $this->sectionedits[] = $data;
-        return 'sectionedit'.$data['secid'];
-    }
-
-    /**
-     * Finish an edit section range
-     *
-     * @param int  $end     The byte position for the edit end; null for the rest of the page
-     *
-     * @author Adrian Lang <lang@cosmocode.de>
-     */
-    public function finishSectionEdit($end = null, $hid = null) {
-        $data = array_pop($this->sectionedits);
-        if(!is_null($end) && $end <= $data['start']) {
-            return;
-        }
-        if(!is_null($hid)) {
-            $data['hid'] .= $hid;
-        }
-        $data['range'] = $data['start'].'-'.(is_null($end) ? '' : $end);
-        unset($data['start']);
-        $this->doc .= '<!-- EDIT'.hsc(json_encode ($data)).' -->';
-    }
-
-    /**
-     * Returns the format produced by this renderer.
-     *
-     * @return string always 'xhtml'
-     */
-    function getFormat() {
-        return 'xhtml';
-    }
-
-    /**
-     * Initialize the document
-     */
-    function document_start() {
-        //reset some internals
-        $this->toc     = array();
-        $this->headers = array();
-    }
-
-    /**
-     * Finalize the document
-     */
-    function document_end() {
-        // Finish open section edits.
-        while(count($this->sectionedits) > 0) {
-            if($this->sectionedits[count($this->sectionedits) - 1]['start'] <= 1) {
-                // If there is only one section, do not write a section edit
-                // marker.
-                array_pop($this->sectionedits);
-            } else {
-                $this->finishSectionEdit();
-            }
-        }
-
-        if(count($this->footnotes) > 0) {
-            $this->doc .= '<div class="footnotes">'.DOKU_LF;
-
-            foreach($this->footnotes as $id => $footnote) {
-                // check its not a placeholder that indicates actual footnote text is elsewhere
-                if(substr($footnote, 0, 5) != "@@FNT") {
-
-                    // open the footnote and set the anchor and backlink
-                    $this->doc .= '<div class="fn">';
-                    $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">';
-                    $this->doc .= $id.')</a></sup> '.DOKU_LF;
-
-                    // get any other footnotes that use the same markup
-                    $alt = array_keys($this->footnotes, "@@FNT$id");
-
-                    if(count($alt)) {
-                        foreach($alt as $ref) {
-                            // set anchor and backlink for the other footnotes
-                            $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">';
-                            $this->doc .= ($ref).')</a></sup> '.DOKU_LF;
-                        }
-                    }
-
-                    // add footnote markup and close this footnote
-                    $this->doc .= '<div class="content">'.$footnote.'</div>';
-                    $this->doc .= '</div>'.DOKU_LF;
-                }
-            }
-            $this->doc .= '</div>'.DOKU_LF;
-        }
-
-        // Prepare the TOC
-        global $conf;
-        if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) {
-            global $TOC;
-            $TOC = $this->toc;
-        }
-
-        // make sure there are no empty paragraphs
-        $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc);
-    }
-
-    /**
-     * Add an item to the TOC
-     *
-     * @param string $id       the hash link
-     * @param string $text     the text to display
-     * @param int    $level    the nesting level
-     */
-    function toc_additem($id, $text, $level) {
-        global $conf;
-
-        //handle TOC
-        if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) {
-            $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1);
-        }
-    }
-
-    /**
-     * Render a heading
-     *
-     * @param string $text  the text to display
-     * @param int    $level header level
-     * @param int    $pos   byte position in the original source
-     */
-    function header($text, $level, $pos) {
-        global $conf;
-
-        if(blank($text)) return; //skip empty headlines
-
-        $hid = $this->_headerToLink($text, true);
-
-        //only add items within configured levels
-        $this->toc_additem($hid, $text, $level);
-
-        // adjust $node to reflect hierarchy of levels
-        $this->node[$level - 1]++;
-        if($level < $this->lastlevel) {
-            for($i = 0; $i < $this->lastlevel - $level; $i++) {
-                $this->node[$this->lastlevel - $i - 1] = 0;
-            }
-        }
-        $this->lastlevel = $level;
-
-        if($level <= $conf['maxseclevel'] &&
-            count($this->sectionedits) > 0 &&
-            $this->sectionedits[count($this->sectionedits) - 1]['target'] === 'section'
-        ) {
-            $this->finishSectionEdit($pos - 1);
-        }
-
-        // write the header
-        $this->doc .= DOKU_LF.'<h'.$level;
-        if($level <= $conf['maxseclevel']) {
-            $data = array();
-            $data['target'] = 'section';
-            $data['name'] = $text;
-            $data['hid'] = $hid;
-            $data['codeblockOffset'] = $this->_codeblock;
-            $this->doc .= ' class="'.$this->startSectionEdit($pos, $data).'"';
-        }
-        $this->doc .= ' id="'.$hid.'">';
-        $this->doc .= $this->_xmlEntities($text);
-        $this->doc .= "</h$level>".DOKU_LF;
-    }
-
-    /**
-     * Open a new section
-     *
-     * @param int $level section level (as determined by the previous header)
-     */
-    function section_open($level) {
-        $this->doc .= '<div class="level'.$level.'">'.DOKU_LF;
-    }
-
-    /**
-     * Close the current section
-     */
-    function section_close() {
-        $this->doc .= DOKU_LF.'</div>'.DOKU_LF;
-    }
-
-    /**
-     * Render plain text data
-     *
-     * @param $text
-     */
-    function cdata($text) {
-        $this->doc .= $this->_xmlEntities($text);
-    }
-
-    /**
-     * Open a paragraph
-     */
-    function p_open() {
-        $this->doc .= DOKU_LF.'<p>'.DOKU_LF;
-    }
-
-    /**
-     * Close a paragraph
-     */
-    function p_close() {
-        $this->doc .= DOKU_LF.'</p>'.DOKU_LF;
-    }
-
-    /**
-     * Create a line break
-     */
-    function linebreak() {
-        $this->doc .= '<br/>'.DOKU_LF;
-    }
-
-    /**
-     * Create a horizontal line
-     */
-    function hr() {
-        $this->doc .= '<hr />'.DOKU_LF;
-    }
-
-    /**
-     * Start strong (bold) formatting
-     */
-    function strong_open() {
-        $this->doc .= '<strong>';
-    }
-
-    /**
-     * Stop strong (bold) formatting
-     */
-    function strong_close() {
-        $this->doc .= '</strong>';
-    }
-
-    /**
-     * Start emphasis (italics) formatting
-     */
-    function emphasis_open() {
-        $this->doc .= '<em>';
-    }
-
-    /**
-     * Stop emphasis (italics) formatting
-     */
-    function emphasis_close() {
-        $this->doc .= '</em>';
-    }
-
-    /**
-     * Start underline formatting
-     */
-    function underline_open() {
-        $this->doc .= '<em class="u">';
-    }
-
-    /**
-     * Stop underline formatting
-     */
-    function underline_close() {
-        $this->doc .= '</em>';
-    }
-
-    /**
-     * Start monospace formatting
-     */
-    function monospace_open() {
-        $this->doc .= '<code>';
-    }
-
-    /**
-     * Stop monospace formatting
-     */
-    function monospace_close() {
-        $this->doc .= '</code>';
-    }
-
-    /**
-     * Start a subscript
-     */
-    function subscript_open() {
-        $this->doc .= '<sub>';
-    }
-
-    /**
-     * Stop a subscript
-     */
-    function subscript_close() {
-        $this->doc .= '</sub>';
-    }
-
-    /**
-     * Start a superscript
-     */
-    function superscript_open() {
-        $this->doc .= '<sup>';
-    }
-
-    /**
-     * Stop a superscript
-     */
-    function superscript_close() {
-        $this->doc .= '</sup>';
-    }
-
-    /**
-     * Start deleted (strike-through) formatting
-     */
-    function deleted_open() {
-        $this->doc .= '<del>';
-    }
-
-    /**
-     * Stop deleted (strike-through) formatting
-     */
-    function deleted_close() {
-        $this->doc .= '</del>';
-    }
-
-    /**
-     * Callback for footnote start syntax
-     *
-     * All following content will go to the footnote instead of
-     * the document. To achieve this the previous rendered content
-     * is moved to $store and $doc is cleared
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function footnote_open() {
-
-        // move current content to store and record footnote
-        $this->store = $this->doc;
-        $this->doc   = '';
-    }
-
-    /**
-     * Callback for footnote end syntax
-     *
-     * All rendered content is moved to the $footnotes array and the old
-     * content is restored from $store again
-     *
-     * @author Andreas Gohr
-     */
-    function footnote_close() {
-        /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */
-        static $fnid = 0;
-        // assign new footnote id (we start at 1)
-        $fnid++;
-
-        // recover footnote into the stack and restore old content
-        $footnote    = $this->doc;
-        $this->doc   = $this->store;
-        $this->store = '';
-
-        // check to see if this footnote has been seen before
-        $i = array_search($footnote, $this->footnotes);
-
-        if($i === false) {
-            // its a new footnote, add it to the $footnotes array
-            $this->footnotes[$fnid] = $footnote;
-        } else {
-            // seen this one before, save a placeholder
-            $this->footnotes[$fnid] = "@@FNT".($i);
-        }
-
-        // output the footnote reference and link
-        $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>';
-    }
-
-    /**
-     * Open an unordered list
-     *
-     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function listu_open($classes = null) {
-        $class = '';
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class = " class=\"$classes\"";
-        }
-        $this->doc .= "<ul$class>".DOKU_LF;
-    }
-
-    /**
-     * Close an unordered list
-     */
-    function listu_close() {
-        $this->doc .= '</ul>'.DOKU_LF;
-    }
-
-    /**
-     * Open an ordered list
-     *
-     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function listo_open($classes = null) {
-        $class = '';
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class = " class=\"$classes\"";
-        }
-        $this->doc .= "<ol$class>".DOKU_LF;
-    }
-
-    /**
-     * Close an ordered list
-     */
-    function listo_close() {
-        $this->doc .= '</ol>'.DOKU_LF;
-    }
-
-    /**
-     * Open a list item
-     *
-     * @param int $level the nesting level
-     * @param bool $node true when a node; false when a leaf
-     */
-    function listitem_open($level, $node=false) {
-        $branching = $node ? ' node' : '';
-        $this->doc .= '<li class="level'.$level.$branching.'">';
-    }
-
-    /**
-     * Close a list item
-     */
-    function listitem_close() {
-        $this->doc .= '</li>'.DOKU_LF;
-    }
-
-    /**
-     * Start the content of a list item
-     */
-    function listcontent_open() {
-        $this->doc .= '<div class="li">';
-    }
-
-    /**
-     * Stop the content of a list item
-     */
-    function listcontent_close() {
-        $this->doc .= '</div>'.DOKU_LF;
-    }
-
-    /**
-     * Output unformatted $text
-     *
-     * Defaults to $this->cdata()
-     *
-     * @param string $text
-     */
-    function unformatted($text) {
-        $this->doc .= $this->_xmlEntities($text);
-    }
-
-    /**
-     * Execute PHP code if allowed
-     *
-     * @param  string $text      PHP code that is either executed or printed
-     * @param  string $wrapper   html element to wrap result if $conf['phpok'] is okff
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function php($text, $wrapper = 'code') {
-        global $conf;
-
-        if($conf['phpok']) {
-            ob_start();
-            eval($text);
-            $this->doc .= ob_get_contents();
-            ob_end_clean();
-        } else {
-            $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper);
-        }
-    }
-
-    /**
-     * Output block level PHP code
-     *
-     * If $conf['phpok'] is true this should evaluate the given code and append the result
-     * to $doc
-     *
-     * @param string $text The PHP code
-     */
-    function phpblock($text) {
-        $this->php($text, 'pre');
-    }
-
-    /**
-     * Insert HTML if allowed
-     *
-     * @param  string $text      html text
-     * @param  string $wrapper   html element to wrap result if $conf['htmlok'] is okff
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function html($text, $wrapper = 'code') {
-        global $conf;
-
-        if($conf['htmlok']) {
-            $this->doc .= $text;
-        } else {
-            $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper);
-        }
-    }
-
-    /**
-     * Output raw block-level HTML
-     *
-     * If $conf['htmlok'] is true this should add the code as is to $doc
-     *
-     * @param string $text The HTML
-     */
-    function htmlblock($text) {
-        $this->html($text, 'pre');
-    }
-
-    /**
-     * Start a block quote
-     */
-    function quote_open() {
-        $this->doc .= '<blockquote><div class="no">'.DOKU_LF;
-    }
-
-    /**
-     * Stop a block quote
-     */
-    function quote_close() {
-        $this->doc .= '</div></blockquote>'.DOKU_LF;
-    }
-
-    /**
-     * Output preformatted text
-     *
-     * @param string $text
-     */
-    function preformatted($text) {
-        $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF;
-    }
-
-    /**
-     * Display text as file content, optionally syntax highlighted
-     *
-     * @param string $text     text to show
-     * @param string $language programming language to use for syntax highlighting
-     * @param string $filename file path label
-     * @param array  $options  assoziative array with additional geshi options
-     */
-    function file($text, $language = null, $filename = null, $options=null) {
-        $this->_highlight('file', $text, $language, $filename, $options);
-    }
-
-    /**
-     * Display text as code content, optionally syntax highlighted
-     *
-     * @param string $text     text to show
-     * @param string $language programming language to use for syntax highlighting
-     * @param string $filename file path label
-     * @param array  $options  assoziative array with additional geshi options
-     */
-    function code($text, $language = null, $filename = null, $options=null) {
-        $this->_highlight('code', $text, $language, $filename, $options);
-    }
-
-    /**
-     * Use GeSHi to highlight language syntax in code and file blocks
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param string $type     code|file
-     * @param string $text     text to show
-     * @param string $language programming language to use for syntax highlighting
-     * @param string $filename file path label
-     * @param array  $options  assoziative array with additional geshi options
-     */
-    function _highlight($type, $text, $language = null, $filename = null, $options = null) {
-        global $ID;
-        global $lang;
-        global $INPUT;
-
-        $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language);
-
-        $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language);
-
-        if($filename) {
-            // add icon
-            list($ext) = mimetype($filename, false);
-            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
-            $class = 'mediafile mf_'.$class;
-
-            $offset = 0;
-            if ($INPUT->has('codeblockOffset')) {
-                $offset = $INPUT->str('codeblockOffset');
-            }
-            $this->doc .= '<dl class="'.$type.'">'.DOKU_LF;
-            $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $offset+$this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">';
-            $this->doc .= hsc($filename);
-            $this->doc .= '</a></dt>'.DOKU_LF.'<dd>';
-        }
-
-        if($text{0} == "\n") {
-            $text = substr($text, 1);
-        }
-        if(substr($text, -1) == "\n") {
-            $text = substr($text, 0, -1);
-        }
-
-        if(empty($language)) { // empty is faster than is_null and can prevent '' string
-            $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF;
-        } else {
-            $class = 'code'; //we always need the code class to make the syntax highlighting apply
-            if($type != 'code') $class .= ' '.$type;
-
-            $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '', $options).'</pre>'.DOKU_LF;
-        }
-
-        if($filename) {
-            $this->doc .= '</dd></dl>'.DOKU_LF;
-        }
-
-        $this->_codeblock++;
-    }
-
-    /**
-     * Format an acronym
-     *
-     * Uses $this->acronyms
-     *
-     * @param string $acronym
-     */
-    function acronym($acronym) {
-
-        if(array_key_exists($acronym, $this->acronyms)) {
-
-            $title = $this->_xmlEntities($this->acronyms[$acronym]);
-
-            $this->doc .= '<abbr title="'.$title
-                .'">'.$this->_xmlEntities($acronym).'</abbr>';
-
-        } else {
-            $this->doc .= $this->_xmlEntities($acronym);
-        }
-    }
-
-    /**
-     * Format a smiley
-     *
-     * Uses $this->smiley
-     *
-     * @param string $smiley
-     */
-    function smiley($smiley) {
-        if(array_key_exists($smiley, $this->smileys)) {
-            $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley].
-                '" class="icon" alt="'.
-                $this->_xmlEntities($smiley).'" />';
-        } else {
-            $this->doc .= $this->_xmlEntities($smiley);
-        }
-    }
-
-    /**
-     * Format an entity
-     *
-     * Entities are basically small text replacements
-     *
-     * Uses $this->entities
-     *
-     * @param string $entity
-     */
-    function entity($entity) {
-        if(array_key_exists($entity, $this->entities)) {
-            $this->doc .= $this->entities[$entity];
-        } else {
-            $this->doc .= $this->_xmlEntities($entity);
-        }
-    }
-
-    /**
-     * Typographically format a multiply sign
-     *
-     * Example: ($x=640, $y=480) should result in "640×480"
-     *
-     * @param string|int $x first value
-     * @param string|int $y second value
-     */
-    function multiplyentity($x, $y) {
-        $this->doc .= "$x&times;$y";
-    }
-
-    /**
-     * Render an opening single quote char (language specific)
-     */
-    function singlequoteopening() {
-        global $lang;
-        $this->doc .= $lang['singlequoteopening'];
-    }
-
-    /**
-     * Render a closing single quote char (language specific)
-     */
-    function singlequoteclosing() {
-        global $lang;
-        $this->doc .= $lang['singlequoteclosing'];
-    }
-
-    /**
-     * Render an apostrophe char (language specific)
-     */
-    function apostrophe() {
-        global $lang;
-        $this->doc .= $lang['apostrophe'];
-    }
-
-    /**
-     * Render an opening double quote char (language specific)
-     */
-    function doublequoteopening() {
-        global $lang;
-        $this->doc .= $lang['doublequoteopening'];
-    }
-
-    /**
-     * Render an closinging double quote char (language specific)
-     */
-    function doublequoteclosing() {
-        global $lang;
-        $this->doc .= $lang['doublequoteclosing'];
-    }
-
-    /**
-     * Render a CamelCase link
-     *
-     * @param string $link       The link name
-     * @param bool   $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     *
-     * @see http://en.wikipedia.org/wiki/CamelCase
-     */
-    function camelcaselink($link, $returnonly = false) {
-        if($returnonly) {
-          return $this->internallink($link, $link, null, true);
-        } else {
-          $this->internallink($link, $link);
-        }
-    }
-
-    /**
-     * Render a page local link
-     *
-     * @param string $hash       hash link identifier
-     * @param string $name       name for the link
-     * @param bool   $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function locallink($hash, $name = null, $returnonly = false) {
-        global $ID;
-        $name  = $this->_getLinkTitle($name, $hash, $isImage);
-        $hash  = $this->_headerToLink($hash);
-        $title = $ID.' ↵';
-
-        $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">';
-        $doc .= $name;
-        $doc .= '</a>';
-
-        if($returnonly) {
-          return $doc;
-        } else {
-          $this->doc .= $doc;
-        }
-    }
-
-    /**
-     * Render an internal Wiki Link
-     *
-     * $search,$returnonly & $linktype are not for the renderer but are used
-     * elsewhere - no need to implement them in other renderers
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param string      $id         pageid
-     * @param string|null $name       link name
-     * @param string|null $search     adds search url param
-     * @param bool        $returnonly whether to return html or write to doc attribute
-     * @param string      $linktype   type to set use of headings
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') {
-        global $conf;
-        global $ID;
-        global $INFO;
-
-        $params = '';
-        $parts  = explode('?', $id, 2);
-        if(count($parts) === 2) {
-            $id     = $parts[0];
-            $params = $parts[1];
-        }
-
-        // For empty $id we need to know the current $ID
-        // We need this check because _simpleTitle needs
-        // correct $id and resolve_pageid() use cleanID($id)
-        // (some things could be lost)
-        if($id === '') {
-            $id = $ID;
-        }
-
-        // default name is based on $id as given
-        $default = $this->_simpleTitle($id);
-
-        // now first resolve and clean up the $id
-        resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true);
-
-        $link = array();
-        $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype);
-        if(!$isImage) {
-            if($exists) {
-                $class = 'wikilink1';
-            } else {
-                $class       = 'wikilink2';
-                $link['rel'] = 'nofollow';
-            }
-        } else {
-            $class = 'media';
-        }
-
-        //keep hash anchor
-        @list($id, $hash) = explode('#', $id, 2);
-        if(!empty($hash)) $hash = $this->_headerToLink($hash);
-
-        //prepare for formating
-        $link['target'] = $conf['target']['wiki'];
-        $link['style']  = '';
-        $link['pre']    = '';
-        $link['suf']    = '';
-        // highlight link to current page
-        if($id == $INFO['id']) {
-            $link['pre'] = '<span class="curid">';
-            $link['suf'] = '</span>';
-        }
-        $link['more']   = '';
-        $link['class']  = $class;
-        if($this->date_at) {
-            $params = $params.'&at='.rawurlencode($this->date_at);
-        }
-        $link['url']    = wl($id, $params);
-        $link['name']   = $name;
-        $link['title']  = $id;
-        //add search string
-        if($search) {
-            ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&amp;';
-            if(is_array($search)) {
-                $search = array_map('rawurlencode', $search);
-                $link['url'] .= 's[]='.join('&amp;s[]=', $search);
-            } else {
-                $link['url'] .= 's='.rawurlencode($search);
-            }
-        }
-
-        //keep hash
-        if($hash) $link['url'] .= '#'.$hash;
-
-        //output formatted
-        if($returnonly) {
-            return $this->_formatLink($link);
-        } else {
-            $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Render an external link
-     *
-     * @param string       $url        full URL with scheme
-     * @param string|array $name       name for the link, array for media file
-     * @param bool         $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function externallink($url, $name = null, $returnonly = false) {
-        global $conf;
-
-        $name = $this->_getLinkTitle($name, $url, $isImage);
-
-        // url might be an attack vector, only allow registered protocols
-        if(is_null($this->schemes)) $this->schemes = getSchemes();
-        list($scheme) = explode('://', $url);
-        $scheme = strtolower($scheme);
-        if(!in_array($scheme, $this->schemes)) $url = '';
-
-        // is there still an URL?
-        if(!$url) {
-            if($returnonly) {
-                return $name;
-            } else {
-                $this->doc .= $name;
-            }
-            return;
-        }
-
-        // set class
-        if(!$isImage) {
-            $class = 'urlextern';
-        } else {
-            $class = 'media';
-        }
-
-        //prepare for formating
-        $link = array();
-        $link['target'] = $conf['target']['extern'];
-        $link['style']  = '';
-        $link['pre']    = '';
-        $link['suf']    = '';
-        $link['more']   = '';
-        $link['class']  = $class;
-        $link['url']    = $url;
-        $link['rel']    = '';
-
-        $link['name']  = $name;
-        $link['title'] = $this->_xmlEntities($url);
-        if($conf['relnofollow']) $link['rel'] .= ' nofollow';
-        if($conf['target']['extern']) $link['rel'] .= ' noopener';
-
-        //output formatted
-        if($returnonly) {
-            return $this->_formatLink($link);
-        } else {
-            $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Render an interwiki link
-     *
-     * You may want to use $this->_resolveInterWiki() here
-     *
-     * @param string       $match      original link - probably not much use
-     * @param string|array $name       name for the link, array for media file
-     * @param string       $wikiName   indentifier (shortcut) for the remote wiki
-     * @param string       $wikiUri    the fragment parsed from the original link
-     * @param bool         $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) {
-        global $conf;
-
-        $link           = array();
-        $link['target'] = $conf['target']['interwiki'];
-        $link['pre']    = '';
-        $link['suf']    = '';
-        $link['more']   = '';
-        $link['name']   = $this->_getLinkTitle($name, $wikiUri, $isImage);
-        $link['rel']    = '';
-
-        //get interwiki URL
-        $exists = null;
-        $url    = $this->_resolveInterWiki($wikiName, $wikiUri, $exists);
-
-        if(!$isImage) {
-            $class         = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName);
-            $link['class'] = "interwiki iw_$class";
-        } else {
-            $link['class'] = 'media';
-        }
-
-        //do we stay at the same server? Use local target
-        if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) {
-            $link['target'] = $conf['target']['wiki'];
-        }
-        if($exists !== null && !$isImage) {
-            if($exists) {
-                $link['class'] .= ' wikilink1';
-            } else {
-                $link['class'] .= ' wikilink2';
-                $link['rel'] .= ' nofollow';
-            }
-        }
-        if($conf['target']['interwiki']) $link['rel'] .= ' noopener';
-
-        $link['url']   = $url;
-        $link['title'] = htmlspecialchars($link['url']);
-
-        //output formatted
-        if($returnonly) {
-            return $this->_formatLink($link);
-        } else {
-            $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Link to windows share
-     *
-     * @param string       $url        the link
-     * @param string|array $name       name for the link, array for media file
-     * @param bool         $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function windowssharelink($url, $name = null, $returnonly = false) {
-        global $conf;
-
-        //simple setup
-        $link = array();
-        $link['target'] = $conf['target']['windows'];
-        $link['pre']    = '';
-        $link['suf']    = '';
-        $link['style']  = '';
-
-        $link['name'] = $this->_getLinkTitle($name, $url, $isImage);
-        if(!$isImage) {
-            $link['class'] = 'windows';
-        } else {
-            $link['class'] = 'media';
-        }
-
-        $link['title'] = $this->_xmlEntities($url);
-        $url           = str_replace('\\', '/', $url);
-        $url           = 'file:///'.$url;
-        $link['url']   = $url;
-
-        //output formatted
-        if($returnonly) {
-            return $this->_formatLink($link);
-        } else {
-            $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Render a linked E-Mail Address
-     *
-     * Honors $conf['mailguard'] setting
-     *
-     * @param string       $address    Email-Address
-     * @param string|array $name       name for the link, array for media file
-     * @param bool         $returnonly whether to return html or write to doc attribute
-     * @return void|string writes to doc attribute or returns html depends on $returnonly
-     */
-    function emaillink($address, $name = null, $returnonly = false) {
-        global $conf;
-        //simple setup
-        $link           = array();
-        $link['target'] = '';
-        $link['pre']    = '';
-        $link['suf']    = '';
-        $link['style']  = '';
-        $link['more']   = '';
-
-        $name = $this->_getLinkTitle($name, '', $isImage);
-        if(!$isImage) {
-            $link['class'] = 'mail';
-        } else {
-            $link['class'] = 'media';
-        }
-
-        $address = $this->_xmlEntities($address);
-        $address = obfuscate($address);
-        $title   = $address;
-
-        if(empty($name)) {
-            $name = $address;
-        }
-
-        if($conf['mailguard'] == 'visible') $address = rawurlencode($address);
-
-        $link['url']   = 'mailto:'.$address;
-        $link['name']  = $name;
-        $link['title'] = $title;
-
-        //output formatted
-        if($returnonly) {
-            return $this->_formatLink($link);
-        } else {
-            $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Render an internal media file
-     *
-     * @param string $src       media ID
-     * @param string $title     descriptive text
-     * @param string $align     left|center|right
-     * @param int    $width     width of media in pixel
-     * @param int    $height    height of media in pixel
-     * @param string $cache     cache|recache|nocache
-     * @param string $linking   linkonly|detail|nolink
-     * @param bool   $return    return HTML instead of adding to $doc
-     * @return void|string writes to doc attribute or returns html depends on $return
-     */
-    function internalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null, $return = false) {
-        global $ID;
-        if (strpos($src, '#') !== false) {
-            list($src, $hash) = explode('#', $src, 2);
-        }
-        resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true);
-
-        $noLink = false;
-        $render = ($linking == 'linkonly') ? false : true;
-        $link   = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
-
-        list($ext, $mime) = mimetype($src, false);
-        if(substr($mime, 0, 5) == 'image' && $render) {
-            $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct'));
-        } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) {
-            // don't link movies
-            $noLink = true;
-        } else {
-            // add file icons
-            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
-            $link['class'] .= ' mediafile mf_'.$class;
-            $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true);
-            if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')';
-        }
-
-        if (!empty($hash)) $link['url'] .= '#'.$hash;
-
-        //markup non existing files
-        if(!$exists) {
-            $link['class'] .= ' wikilink2';
-        }
-
-        //output formatted
-        if($return) {
-            if($linking == 'nolink' || $noLink) return $link['name'];
-            else return $this->_formatLink($link);
-        } else {
-            if($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
-            else $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Render an external media file
-     *
-     * @param string $src     full media URL
-     * @param string $title   descriptive text
-     * @param string $align   left|center|right
-     * @param int    $width   width of media in pixel
-     * @param int    $height  height of media in pixel
-     * @param string $cache   cache|recache|nocache
-     * @param string $linking linkonly|detail|nolink
-     * @param bool   $return  return HTML instead of adding to $doc
-     * @return void|string writes to doc attribute or returns html depends on $return
-     */
-    function externalmedia($src, $title = null, $align = null, $width = null,
-                           $height = null, $cache = null, $linking = null, $return = false) {
-        if(link_isinterwiki($src)){
-            list($shortcut, $reference) = explode('>', $src, 2);
-            $exists = null;
-            $src = $this->_resolveInterWiki($shortcut, $reference, $exists);
-        }
-        list($src, $hash) = explode('#', $src, 2);
-        $noLink = false;
-        $render = ($linking == 'linkonly') ? false : true;
-        $link   = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
-
-        $link['url'] = ml($src, array('cache' => $cache));
-
-        list($ext, $mime) = mimetype($src, false);
-        if(substr($mime, 0, 5) == 'image' && $render) {
-            // link only jpeg images
-            // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true;
-        } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) {
-            // don't link movies
-            $noLink = true;
-        } else {
-            // add file icons
-            $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
-            $link['class'] .= ' mediafile mf_'.$class;
-        }
-
-        if($hash) $link['url'] .= '#'.$hash;
-
-        //output formatted
-        if($return) {
-            if($linking == 'nolink' || $noLink) return $link['name'];
-            else return $this->_formatLink($link);
-        } else {
-            if($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
-            else $this->doc .= $this->_formatLink($link);
-        }
-    }
-
-    /**
-     * Renders an RSS feed
-     *
-     * @param string $url    URL of the feed
-     * @param array  $params Finetuning of the output
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function rss($url, $params) {
-        global $lang;
-        global $conf;
-
-        require_once(DOKU_INC.'inc/FeedParser.php');
-        $feed = new FeedParser();
-        $feed->set_feed_url($url);
-
-        //disable warning while fetching
-        if(!defined('DOKU_E_LEVEL')) {
-            $elvl = error_reporting(E_ERROR);
-        }
-        $rc = $feed->init();
-        if(isset($elvl)) {
-            error_reporting($elvl);
-        }
-
-        if($params['nosort']) $feed->enable_order_by_date(false);
-
-        //decide on start and end
-        if($params['reverse']) {
-            $mod   = -1;
-            $start = $feed->get_item_quantity() - 1;
-            $end   = $start - ($params['max']);
-            $end   = ($end < -1) ? -1 : $end;
-        } else {
-            $mod   = 1;
-            $start = 0;
-            $end   = $feed->get_item_quantity();
-            $end   = ($end > $params['max']) ? $params['max'] : $end;
-        }
-
-        $this->doc .= '<ul class="rss">';
-        if($rc) {
-            for($x = $start; $x != $end; $x += $mod) {
-                $item = $feed->get_item($x);
-                $this->doc .= '<li><div class="li">';
-                // support feeds without links
-                $lnkurl = $item->get_permalink();
-                if($lnkurl) {
-                    // title is escaped by SimplePie, we unescape here because it
-                    // is escaped again in externallink() FS#1705
-                    $this->externallink(
-                        $item->get_permalink(),
-                        html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8')
-                    );
-                } else {
-                    $this->doc .= ' '.$item->get_title();
-                }
-                if($params['author']) {
-                    $author = $item->get_author(0);
-                    if($author) {
-                        $name = $author->get_name();
-                        if(!$name) $name = $author->get_email();
-                        if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name);
-                    }
-                }
-                if($params['date']) {
-                    $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')';
-                }
-                if($params['details']) {
-                    $this->doc .= '<div class="detail">';
-                    if($conf['htmlok']) {
-                        $this->doc .= $item->get_description();
-                    } else {
-                        $this->doc .= strip_tags($item->get_description());
-                    }
-                    $this->doc .= '</div>';
-                }
-
-                $this->doc .= '</div></li>';
-            }
-        } else {
-            $this->doc .= '<li><div class="li">';
-            $this->doc .= '<em>'.$lang['rssfailed'].'</em>';
-            $this->externallink($url);
-            if($conf['allowdebug']) {
-                $this->doc .= '<!--'.hsc($feed->error).'-->';
-            }
-            $this->doc .= '</div></li>';
-        }
-        $this->doc .= '</ul>';
-    }
-
-    /**
-     * Start a table
-     *
-     * @param int $maxcols maximum number of columns
-     * @param int $numrows NOT IMPLEMENTED
-     * @param int $pos byte position in the original source
-     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) {
-        // initialize the row counter used for classes
-        $this->_counter['row_counter'] = 0;
-        $class                         = 'table';
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class .= ' ' . $classes;
-        }
-        if($pos !== null) {
-            $hid = $this->_headerToLink($class, true);
-            $data = array();
-            $data['target'] = 'table';
-            $data['name'] = '';
-            $data['hid'] = $hid;
-            $class .= ' '.$this->startSectionEdit($pos, $data);
-        }
-        $this->doc .= '<div class="'.$class.'"><table class="inline">'.
-            DOKU_LF;
-    }
-
-    /**
-     * Close a table
-     *
-     * @param int $pos byte position in the original source
-     */
-    function table_close($pos = null) {
-        $this->doc .= '</table></div>'.DOKU_LF;
-        if($pos !== null) {
-            $this->finishSectionEdit($pos);
-        }
-    }
-
-    /**
-     * Open a table header
-     */
-    function tablethead_open() {
-        $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF;
-    }
-
-    /**
-     * Close a table header
-     */
-    function tablethead_close() {
-        $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF;
-    }
-
-    /**
-     * Open a table body
-     */
-    function tabletbody_open() {
-        $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF;
-    }
-
-    /**
-     * Close a table body
-     */
-    function tabletbody_close() {
-        $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF;
-    }
-
-    /**
-     * Open a table footer
-     */
-    function tabletfoot_open() {
-        $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF;
-    }
-
-    /**
-     * Close a table footer
-     */
-    function tabletfoot_close() {
-        $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF;
-    }
-
-    /**
-     * Open a table row
-     *
-     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function tablerow_open($classes = null) {
-        // initialize the cell counter used for classes
-        $this->_counter['cell_counter'] = 0;
-        $class                          = 'row'.$this->_counter['row_counter']++;
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class .= ' ' . $classes;
-        }
-        $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB;
-    }
-
-    /**
-     * Close a table row
-     */
-    function tablerow_close() {
-        $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF;
-    }
-
-    /**
-     * Open a table header cell
-     *
-     * @param int    $colspan
-     * @param string $align left|center|right
-     * @param int    $rowspan
-     * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
-        $class = 'class="col'.$this->_counter['cell_counter']++;
-        if(!is_null($align)) {
-            $class .= ' '.$align.'align';
-        }
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class .= ' ' . $classes;
-        }
-        $class .= '"';
-        $this->doc .= '<th '.$class;
-        if($colspan > 1) {
-            $this->_counter['cell_counter'] += $colspan - 1;
-            $this->doc .= ' colspan="'.$colspan.'"';
-        }
-        if($rowspan > 1) {
-            $this->doc .= ' rowspan="'.$rowspan.'"';
-        }
-        $this->doc .= '>';
-    }
-
-    /**
-     * Close a table header cell
-     */
-    function tableheader_close() {
-        $this->doc .= '</th>';
-    }
-
-    /**
-     * Open a table cell
-     *
-     * @param int       $colspan
-     * @param string    $align left|center|right
-     * @param int       $rowspan
-     * @param string|string[]    $classes css classes - have to be valid, do not pass unfiltered user input
-     */
-    function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
-        $class = 'class="col'.$this->_counter['cell_counter']++;
-        if(!is_null($align)) {
-            $class .= ' '.$align.'align';
-        }
-        if($classes !== null) {
-            if(is_array($classes)) $classes = join(' ', $classes);
-            $class .= ' ' . $classes;
-        }
-        $class .= '"';
-        $this->doc .= '<td '.$class;
-        if($colspan > 1) {
-            $this->_counter['cell_counter'] += $colspan - 1;
-            $this->doc .= ' colspan="'.$colspan.'"';
-        }
-        if($rowspan > 1) {
-            $this->doc .= ' rowspan="'.$rowspan.'"';
-        }
-        $this->doc .= '>';
-    }
-
-    /**
-     * Close a table cell
-     */
-    function tablecell_close() {
-        $this->doc .= '</td>';
-    }
-
-    /**
-     * Returns the current header level.
-     * (required e.g. by the filelist plugin)
-     *
-     * @return int The current header level
-     */
-    function getLastlevel() {
-        return $this->lastlevel;
-    }
-
-    #region Utility functions
-
-    /**
-     * Build a link
-     *
-     * Assembles all parts defined in $link returns HTML for the link
-     *
-     * @param array $link attributes of a link
-     * @return string
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function _formatLink($link) {
-        //make sure the url is XHTML compliant (skip mailto)
-        if(substr($link['url'], 0, 7) != 'mailto:') {
-            $link['url'] = str_replace('&', '&amp;', $link['url']);
-            $link['url'] = str_replace('&amp;amp;', '&amp;', $link['url']);
-        }
-        //remove double encodings in titles
-        $link['title'] = str_replace('&amp;amp;', '&amp;', $link['title']);
-
-        // be sure there are no bad chars in url or title
-        // (we can't do this for name because it can contain an img tag)
-        $link['url']   = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22'));
-        $link['title'] = strtr($link['title'], array('>' => '&gt;', '<' => '&lt;', '"' => '&quot;'));
-
-        $ret = '';
-        $ret .= $link['pre'];
-        $ret .= '<a href="'.$link['url'].'"';
-        if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"';
-        if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"';
-        if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"';
-        if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"';
-        if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"';
-        if(!empty($link['more'])) $ret .= ' '.$link['more'];
-        $ret .= '>';
-        $ret .= $link['name'];
-        $ret .= '</a>';
-        $ret .= $link['suf'];
-        return $ret;
-    }
-
-    /**
-     * Renders internal and external media
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param string $src       media ID
-     * @param string $title     descriptive text
-     * @param string $align     left|center|right
-     * @param int    $width     width of media in pixel
-     * @param int    $height    height of media in pixel
-     * @param string $cache     cache|recache|nocache
-     * @param bool   $render    should the media be embedded inline or just linked
-     * @return string
-     */
-    function _media($src, $title = null, $align = null, $width = null,
-                    $height = null, $cache = null, $render = true) {
-
-        $ret = '';
-
-        list($ext, $mime) = mimetype($src);
-        if(substr($mime, 0, 5) == 'image') {
-            // first get the $title
-            if(!is_null($title)) {
-                $title = $this->_xmlEntities($title);
-            } elseif($ext == 'jpg' || $ext == 'jpeg') {
-                //try to use the caption from IPTC/EXIF
-                require_once(DOKU_INC.'inc/JpegMeta.php');
-                $jpeg = new JpegMeta(mediaFN($src));
-                if($jpeg !== false) $cap = $jpeg->getTitle();
-                if(!empty($cap)) {
-                    $title = $this->_xmlEntities($cap);
-                }
-            }
-            if(!$render) {
-                // if the picture is not supposed to be rendered
-                // return the title of the picture
-                if(!$title) {
-                    // just show the sourcename
-                    $title = $this->_xmlEntities(utf8_basename(noNS($src)));
-                }
-                return $title;
-            }
-            //add image tag
-            $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"';
-            $ret .= ' class="media'.$align.'"';
-
-            if($title) {
-                $ret .= ' title="'.$title.'"';
-                $ret .= ' alt="'.$title.'"';
-            } else {
-                $ret .= ' alt=""';
-            }
-
-            if(!is_null($width))
-                $ret .= ' width="'.$this->_xmlEntities($width).'"';
-
-            if(!is_null($height))
-                $ret .= ' height="'.$this->_xmlEntities($height).'"';
-
-            $ret .= ' />';
-
-        } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) {
-            // first get the $title
-            $title = !is_null($title) ? $this->_xmlEntities($title) : false;
-            if(!$render) {
-                // if the file is not supposed to be rendered
-                // return the title of the file (just the sourcename if there is no title)
-                return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src)));
-            }
-
-            $att          = array();
-            $att['class'] = "media$align";
-            if($title) {
-                $att['title'] = $title;
-            }
-
-            if(media_supportedav($mime, 'video')) {
-                //add video
-                $ret .= $this->_video($src, $width, $height, $att);
-            }
-            if(media_supportedav($mime, 'audio')) {
-                //add audio
-                $ret .= $this->_audio($src, $att);
-            }
-
-        } elseif($mime == 'application/x-shockwave-flash') {
-            if(!$render) {
-                // if the flash is not supposed to be rendered
-                // return the title of the flash
-                if(!$title) {
-                    // just show the sourcename
-                    $title = utf8_basename(noNS($src));
-                }
-                return $this->_xmlEntities($title);
-            }
-
-            $att          = array();
-            $att['class'] = "media$align";
-            if($align == 'right') $att['align'] = 'right';
-            if($align == 'left') $att['align'] = 'left';
-            $ret .= html_flashobject(
-                ml($src, array('cache' => $cache), true, '&'), $width, $height,
-                array('quality' => 'high'),
-                null,
-                $att,
-                $this->_xmlEntities($title)
-            );
-        } elseif($title) {
-            // well at least we have a title to display
-            $ret .= $this->_xmlEntities($title);
-        } else {
-            // just show the sourcename
-            $ret .= $this->_xmlEntities(utf8_basename(noNS($src)));
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Escape string for output
-     *
-     * @param $string
-     * @return string
-     */
-    function _xmlEntities($string) {
-        return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
-    }
-
-    /**
-     * Creates a linkid from a headline
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param string  $title   The headline title
-     * @param boolean $create  Create a new unique ID?
-     * @return string
-     */
-    function _headerToLink($title, $create = false) {
-        if($create) {
-            return sectionID($title, $this->headers);
-        } else {
-            $check = false;
-            return sectionID($title, $check);
-        }
-    }
-
-    /**
-     * Construct a title and handle images in titles
-     *
-     * @author Harry Fuecks <hfuecks@gmail.com>
-     * @param string|array $title    either string title or media array
-     * @param string       $default  default title if nothing else is found
-     * @param bool         $isImage  will be set to true if it's a media file
-     * @param null|string  $id       linked page id (used to extract title from first heading)
-     * @param string       $linktype content|navigation
-     * @return string      HTML of the title, might be full image tag or just escaped text
-     */
-    function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') {
-        $isImage = false;
-        if(is_array($title)) {
-            $isImage = true;
-            return $this->_imageTitle($title);
-        } elseif(is_null($title) || trim($title) == '') {
-            if(useHeading($linktype) && $id) {
-                $heading = p_get_first_heading($id);
-                if(!blank($heading)) {
-                    return $this->_xmlEntities($heading);
-                }
-            }
-            return $this->_xmlEntities($default);
-        } else {
-            return $this->_xmlEntities($title);
-        }
-    }
-
-    /**
-     * Returns HTML code for images used in link titles
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     * @param array $img
-     * @return string HTML img tag or similar
-     */
-    function _imageTitle($img) {
-        global $ID;
-
-        // some fixes on $img['src']
-        // see internalmedia() and externalmedia()
-        list($img['src']) = explode('#', $img['src'], 2);
-        if($img['type'] == 'internalmedia') {
-            resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true);
-        }
-
-        return $this->_media(
-            $img['src'],
-            $img['title'],
-            $img['align'],
-            $img['width'],
-            $img['height'],
-            $img['cache']
-        );
-    }
-
-    /**
-     * helperfunction to return a basic link to a media
-     *
-     * used in internalmedia() and externalmedia()
-     *
-     * @author   Pierre Spring <pierre.spring@liip.ch>
-     * @param string $src       media ID
-     * @param string $title     descriptive text
-     * @param string $align     left|center|right
-     * @param int    $width     width of media in pixel
-     * @param int    $height    height of media in pixel
-     * @param string $cache     cache|recache|nocache
-     * @param bool   $render    should the media be embedded inline or just linked
-     * @return array associative array with link config
-     */
-    function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) {
-        global $conf;
-
-        $link           = array();
-        $link['class']  = 'media';
-        $link['style']  = '';
-        $link['pre']    = '';
-        $link['suf']    = '';
-        $link['more']   = '';
-        $link['target'] = $conf['target']['media'];
-        if($conf['target']['media']) $link['rel'] = 'noopener';
-        $link['title']  = $this->_xmlEntities($src);
-        $link['name']   = $this->_media($src, $title, $align, $width, $height, $cache, $render);
-
-        return $link;
-    }
-
-    /**
-     * Embed video(s) in HTML
-     *
-     * @author Anika Henke <anika@selfthinker.org>
-     * @author Schplurtz le Déboulonné <Schplurtz@laposte.net>
-     *
-     * @param string $src         - ID of video to embed
-     * @param int    $width       - width of the video in pixels
-     * @param int    $height      - height of the video in pixels
-     * @param array  $atts        - additional attributes for the <video> tag
-     * @return string
-     */
-    function _video($src, $width, $height, $atts = null) {
-        // prepare width and height
-        if(is_null($atts)) $atts = array();
-        $atts['width']  = (int) $width;
-        $atts['height'] = (int) $height;
-        if(!$atts['width']) $atts['width'] = 320;
-        if(!$atts['height']) $atts['height'] = 240;
-
-        $posterUrl = '';
-        $files = array();
-        $tracks = array();
-        $isExternal = media_isexternal($src);
-
-        if ($isExternal) {
-            // take direct source for external files
-            list(/*ext*/, $srcMime) = mimetype($src);
-            $files[$srcMime] = $src;
-        } else {
-            // prepare alternative formats
-            $extensions   = array('webm', 'ogv', 'mp4');
-            $files        = media_alternativefiles($src, $extensions);
-            $poster       = media_alternativefiles($src, array('jpg', 'png'));
-            $tracks       = media_trackfiles($src);
-            if(!empty($poster)) {
-                $posterUrl = ml(reset($poster), '', true, '&');
-            }
-        }
-
-        $out = '';
-        // open video tag
-        $out .= '<video '.buildAttributes($atts).' controls="controls"';
-        if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"';
-        $out .= '>'.NL;
-        $fallback = '';
-
-        // output source for each alternative video format
-        foreach($files as $mime => $file) {
-            if ($isExternal) {
-                $url = $file;
-                $linkType = 'externalmedia';
-            } else {
-                $url = ml($file, '', true, '&');
-                $linkType = 'internalmedia';
-            }
-            $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file)));
-
-            $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL;
-            // alternative content (just a link to the file)
-            $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true);
-        }
-
-        // output each track if any
-        foreach( $tracks as $trackid => $info ) {
-            list( $kind, $srclang ) = array_map( 'hsc', $info );
-            $out .= "<track kind=\"$kind\" srclang=\"$srclang\" ";
-            $out .= "label=\"$srclang\" ";
-            $out .= 'src="'.ml($trackid, '', true).'">'.NL;
-        }
-
-        // finish
-        $out .= $fallback;
-        $out .= '</video>'.NL;
-        return $out;
-    }
-
-    /**
-     * Embed audio in HTML
-     *
-     * @author Anika Henke <anika@selfthinker.org>
-     *
-     * @param string $src       - ID of audio to embed
-     * @param array  $atts      - additional attributes for the <audio> tag
-     * @return string
-     */
-    function _audio($src, $atts = array()) {
-        $files = array();
-        $isExternal = media_isexternal($src);
-
-        if ($isExternal) {
-            // take direct source for external files
-            list(/*ext*/, $srcMime) = mimetype($src);
-            $files[$srcMime] = $src;
-        } else {
-            // prepare alternative formats
-            $extensions   = array('ogg', 'mp3', 'wav');
-            $files        = media_alternativefiles($src, $extensions);
-        }
-
-        $out = '';
-        // open audio tag
-        $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL;
-        $fallback = '';
-
-        // output source for each alternative audio format
-        foreach($files as $mime => $file) {
-            if ($isExternal) {
-                $url = $file;
-                $linkType = 'externalmedia';
-            } else {
-                $url = ml($file, '', true, '&');
-                $linkType = 'internalmedia';
-            }
-            $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file)));
-
-            $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL;
-            // alternative content (just a link to the file)
-            $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true);
-        }
-
-        // finish
-        $out .= $fallback;
-        $out .= '</audio>'.NL;
-        return $out;
-    }
-
-    /**
-     * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media()
-     * which returns an existing media revision less or equal to rev or date_at
-     *
-     * @author lisps
-     * @param string $media_id
-     * @access protected
-     * @return string revision ('' for current)
-     */
-    function _getLastMediaRevisionAt($media_id){
-        if(!$this->date_at || media_isexternal($media_id)) return '';
-        $pagelog = new MediaChangeLog($media_id);
-        return $pagelog->getLastRevisionAt($this->date_at);
-    }
-
-    #endregion
-}
-
-//Setup VIM: ex: et ts=4 :
diff --git a/wiki/inc/parser/xhtmlsummary.php b/wiki/inc/parser/xhtmlsummary.php
deleted file mode 100644
index 867b71f..0000000
--- a/wiki/inc/parser/xhtmlsummary.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-if(!defined('DOKU_INC')) die('meh.');
-
-/**
- * The summary XHTML form selects either up to the first two paragraphs
- * it find in a page or the first section (whichever comes first)
- * It strips out the table of contents if one exists
- * Section divs are not used - everything should be nested in a single
- * div with CSS class "page"
- * Headings have their a name link removed and section editing links
- * removed
- * It also attempts to capture the first heading in a page for
- * use as the title of the page.
- *
- *
- * @author Harry Fuecks <hfuecks@gmail.com>
- * @todo   Is this currently used anywhere? Should it?
- */
-class Doku_Renderer_xhtmlsummary extends Doku_Renderer_xhtml {
-
-    // Namespace these variables to
-    // avoid clashes with parent classes
-    var $sum_paragraphs = 0;
-    var $sum_capture = true;
-    var $sum_inSection = false;
-    var $sum_summary = '';
-    var $sum_pageTitle = false;
-
-    function document_start() {
-        $this->doc .= DOKU_LF.'<div>'.DOKU_LF;
-    }
-
-    function document_end() {
-        $this->doc = $this->sum_summary;
-        $this->doc .= DOKU_LF.'</div>'.DOKU_LF;
-    }
-
-    // FIXME not supported anymore
-    function toc_open() {
-        $this->sum_summary .= $this->doc;
-    }
-
-    // FIXME not supported anymore
-    function toc_close() {
-        $this->doc = '';
-    }
-
-    function header($text, $level, $pos) {
-        if ( !$this->sum_pageTitle ) {
-            $this->info['sum_pagetitle'] = $text;
-            $this->sum_pageTitle = true;
-        }
-        $this->doc .= DOKU_LF.'<h'.$level.'>';
-        $this->doc .= $this->_xmlEntities($text);
-        $this->doc .= "</h$level>".DOKU_LF;
-    }
-
-    function section_open($level) {
-        if ( $this->sum_capture ) {
-            $this->sum_inSection = true;
-        }
-    }
-
-    function section_close() {
-        if ( $this->sum_capture && $this->sum_inSection ) {
-            $this->sum_summary .= $this->doc;
-            $this->sum_capture = false;
-        }
-    }
-
-    function p_open() {
-        if ( $this->sum_capture && $this->sum_paragraphs < 2 ) {
-            $this->sum_paragraphs++;
-        }
-        parent :: p_open();
-    }
-
-    function p_close() {
-        parent :: p_close();
-        if ( $this->sum_capture && $this->sum_paragraphs >= 2 ) {
-            $this->sum_summary .= $this->doc;
-            $this->sum_capture = false;
-        }
-    }
-
-}
-
-
-//Setup VIM: ex: et ts=2 :