*/ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); require_once DOKU_INC.'inc/parser/renderer.php'; class renderer_plugin_edittable_inverse extends Doku_Renderer { /** @var string will contain the whole document */ public $doc = ''; // bunch of internal state variables private $prepend_not_block = ''; private $_key = 0; private $_pos = 0; private $_ownspan = 0; private $previous_block = false; private $_row = 0; private $_rowspans = array(); private $_table = array(); private $_liststack = array(); private $quotelvl = 0; private $extlinkparser = null; protected $extlinkPatterns = []; function getFormat() { return 'wiki'; } function document_start() { } function document_end() { $this->block(); $this->doc = rtrim($this->doc); } function header($text, $level, $pos) { $this->block(); if(!$text) return; //skip empty headlines // write the header $markup = str_repeat('=', 7 - $level); $this->doc .= "$markup $text $markup".DOKU_LF; } function section_open($level) { $this->block(); # $this->doc .= DOKU_LF; } function section_close() { $this->block(); $this->doc .= DOKU_LF; } // FIXME this did something compllicated with surrounding whitespaces. Why? function cdata($text) { if(strlen($text) === 0) { $this->not_block(); return; } // if(!$this->previous_block && trim(substr($text, 0, 1)) === '' && trim($text) !== '') { // $this->doc .= ' '; // } $this->not_block(); // if(trim(substr($text, -1, 1)) === '' && trim($text) !== '') { // $this->prepend_not_block = ' '; // } // $this->doc .= trim($text); $this->doc .= $text; } function p_close() { $this->block(); if($this->quotelvl === 0) { $this->doc = rtrim($this->doc, DOKU_LF).DOKU_LF.DOKU_LF; } } function p_open() { $this->block(); if(strlen($this->doc) > 0 && substr($this->doc, 1, -1) !== DOKU_LF) { $this->doc .= DOKU_LF.DOKU_LF; } $this->doc .= str_repeat('>', $this->quotelvl); } function linebreak() { $this->not_block(); $this->doc .= '\\\\ '; } function hr() { $this->block(); $this->doc .= '----'; } function block() { if(isset($this->prepend_not_block)) { unset($this->prepend_not_block); } $this->previous_block = true; } function not_block() { if(isset($this->prepend_not_block)) { $this->doc .= $this->prepend_not_block; unset($this->prepend_not_block); } $this->previous_block = false; } function strong_open() { $this->not_block(); $this->doc .= '**'; } function strong_close() { $this->not_block(); $this->doc .= '**'; } function emphasis_open() { $this->not_block(); $this->doc .= '//'; } function emphasis_close() { $this->not_block(); $this->doc .= '//'; } function underline_open() { $this->not_block(); $this->doc .= '__'; } function underline_close() { $this->not_block(); $this->doc .= '__'; } function monospace_open() { $this->not_block(); $this->doc .= "''"; } function monospace_close() { $this->not_block(); $this->doc .= "''"; } function subscript_open() { $this->not_block(); $this->doc .= ''; } function subscript_close() { $this->not_block(); $this->doc .= ''; } function superscript_open() { $this->not_block(); $this->doc .= ''; } function superscript_close() { $this->not_block(); $this->doc .= ''; } function deleted_open() { $this->not_block(); $this->doc .= ''; } function deleted_close() { $this->not_block(); $this->doc .= ''; } function footnote_open() { $this->not_block(); $this->doc .= '(('; } function footnote_close() { $this->not_block(); $this->doc .= '))'; } function listu_open() { $this->block(); if(!isset($this->_liststack)) { $this->_liststack = array(); } if(count($this->_liststack) === 0) { $this->doc .= DOKU_LF; } $this->_liststack[] = '*'; } function listu_close() { $this->block(); array_pop($this->_liststack); if(count($this->_liststack) === 0) { $this->doc .= DOKU_LF; } } function listo_open() { $this->block(); if(!isset($this->_liststack)) { $this->_liststack = array(); } if(count($this->_liststack) === 0) { $this->doc .= DOKU_LF; } $this->_liststack[] = '-'; } function listo_close() { $this->block(); array_pop($this->_liststack); if(count($this->_liststack) === 0) { $this->doc .= DOKU_LF; } } function listitem_open($level, $node = false) { $this->block(); $this->doc .= str_repeat(' ', $level * 2).end($this->_liststack).' '; } function listcontent_close() { $this->block(); $this->doc .= DOKU_LF; } function unformatted($text) { $this->not_block(); if(strpos($text, '%%') !== false) { $this->doc .= "$text"; } elseif($text[0] == "\n") { $this->doc .= "$text"; } else { $this->doc .= "%%$text%%"; } } function php($text, $wrapper = 'code') { $this->not_block(); $this->doc .= "$text"; } function phpblock($text) { $this->block(); $this->doc .= "$text"; } function html($text, $wrapper = 'code') { $this->not_block(); $this->doc .= "$text"; } function htmlblock($text) { $this->block(); $this->doc .= "$text"; } function quote_open() { $this->block(); if(substr($this->doc, -(++$this->quotelvl)) === DOKU_LF.str_repeat('>', $this->quotelvl - 1)) { $this->doc .= '>'; } else { $this->doc .= DOKU_LF.str_repeat('>', $this->quotelvl); } $this->prepend_not_block = ' '; } function quote_close() { $this->block(); $this->quotelvl--; if(strrpos($this->doc, DOKU_LF) === strlen($this->doc) - 1) { return; } $this->doc .= DOKU_LF.DOKU_LF; } function preformatted($text) { $this->block(); $this->doc .= preg_replace('/^/m', ' ', $text).DOKU_LF; } function file($text, $language = null, $filename = null) { $this->_highlight('file', $text, $language, $filename); } function code($text, $language = null, $filename = null) { $this->_highlight('code', $text, $language, $filename); } function _highlight($type, $text, $language = null, $filename = null) { if( $this->previous_block ) $this->doc .= "\n"; $this->block(); $this->doc .= "<$type"; if($language != null) { $this->doc .= " $language"; } if($filename != null) { $this->doc .= " $filename"; } $this->doc .= ">"; $this->doc .= $text; if($text[0] == "\n") $this->doc .= "\n"; $this->doc .= ""; } function acronym($acronym) { $this->not_block(); $this->doc .= $acronym; } function smiley($smiley) { $this->not_block(); $this->doc .= $smiley; } function entity($entity) { $this->not_block(); $this->doc .= $entity; } function multiplyentity($x, $y) { $this->not_block(); $this->doc .= "{$x}x{$y}"; } function singlequoteopening() { $this->not_block(); $this->doc .= "'"; } function singlequoteclosing() { $this->not_block(); $this->doc .= "'"; } function apostrophe() { $this->not_block(); $this->doc .= "'"; } function doublequoteopening() { $this->not_block(); $this->doc .= '"'; } function doublequoteclosing() { $this->not_block(); $this->doc .= '"'; } /** */ function camelcaselink($link) { $this->not_block(); $this->doc .= $link; } function locallink($hash, $name = null) { $this->not_block(); $this->doc .= "[[#$hash"; if($name !== null) { $this->doc .= '|'; $this->_echoLinkTitle($name); } $this->doc .= ']]'; } function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { $this->not_block(); $this->doc .= "[[$id"; if($name !== null) { $this->doc .= '|'; $this->_echoLinkTitle($name); } $this->doc .= ']]'; } /** * Handle external Links * * @author Andreas Gohr * @param $url * @param null $name */ function externallink($url, $name = null) { $this->not_block(); /* * When $name is null it might have been a match of an URL that was in the text without * any link syntax. These are recognized by a bunch of patterns in Doku_Parser_Mode_externallink. * We simply reuse these patterns here. However, since we don't parse the pattern through the Lexer, * no escaping is done on the patterns - this means we need a non-conflicting delimiter. I decided for * a single tick >>'<< which seems to work. Since the patterns contain wordboundaries they are matched * against the URL surrounded by spaces. */ if($name === null) { // get the patterns from the parser if available, otherwise use a duplicate if(is_null($this->extlinkparser)) { if ( class_exists('\dokuwiki\Parsing\ParserMode\Externallink') && method_exists('\dokuwiki\Parsing\ParserMode\Externallink', 'getPatterns') ) { $this->extlinkparser = new \dokuwiki\Parsing\ParserMode\Externallink(); $this->extlinkparser->preConnect(); $this->extlinkPatterns = $this->extlinkparser->getPatterns(); } else { $ltrs = '\w'; $gunk = '/\#~:.?+=&%@!\-\[\]'; $punc = '.:?\-;,'; $host = $ltrs . $punc; $any = $ltrs . $gunk . $punc; $schemes = getSchemes(); foreach ($schemes as $scheme) { $this->extlinkPatterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?(?=['.$punc.']*[^'.$any.'])'; } $this->extlinkPatterns[] = '(?<=\s)(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])'; $this->extlinkPatterns[] = '(?<=\s)(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])'; } } // check if URL matches pattern foreach($this->extlinkPatterns as $pattern) { if(preg_match("'$pattern'", " $url ")) { $this->doc .= $url; // gotcha! return; } } } // still here? if($url === "http://$name" || $url === "ftp://$name") { // special case - www.* or ftp.* matching $this->doc .= $name; } else { // link syntax! definitively link syntax $this->doc .= "[[$url"; if(!is_null($name)) { // we do have a name! $this->doc .= '|'; $this->_echoLinkTitle($name); } $this->doc .= ']]'; } } function interwikilink($match, $name = null, $wikiName, $wikiUri) { $this->not_block(); $this->doc .= "[[$wikiName>$wikiUri"; if($name !== null) { $this->doc .= '|'; $this->_echoLinkTitle($name); } $this->doc .= ']]'; } function windowssharelink($url, $name = null) { $this->not_block(); $this->doc .= "[[$url"; if($name !== null) { $this->doc .= '|'; $this->_echoLinkTitle($name); } $this->doc .= "]]"; } function emaillink($address, $name = null) { $this->not_block(); if($name === null) { $this->doc .= "<$address>"; } else { $this->doc .= "[[$address|"; $this->_echoLinkTitle($name); $this->doc .= ']]'; } } function internalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) { $this->not_block(); $this->doc .= '{{'; if($align === 'center' || $align === 'right') { $this->doc .= ' '; } $this->doc .= $src; $params = array(); if($width !== null) { $params[0] = $width; if($height !== null) { $params[0] .= "x$height"; } } if($cache !== 'cache') { $params[] = $cache; } if($linking !== 'details') { $params[] = $linking; } if(count($params) > 0) { $this->doc .= '?'; } $this->doc .= join('&', $params); if($align === 'center' || $align === 'left') { $this->doc .= ' '; } if($title != null) { $this->doc .= "|$title"; } $this->doc .= '}}'; } function externalmedia($src, $title = null, $align = null, $width = null, $height = null, $cache = null, $linking = null) { $this->internalmedia($src, $title, $align, $width, $height, $cache, $linking); } /** * Renders an RSS feed * * @author Andreas Gohr */ function rss($url, $params) { $this->block(); $this->doc .= '{{rss>'.$url; $vals = array(); if($params['max'] !== 8) { $vals[] = $params['max']; } if($params['reverse']) { $vals[] = 'reverse'; } if($params['author']) { $vals[] = 'author'; } if($params['date']) { $vals[] = 'date'; } if($params['details']) { $vals[] = 'desc'; } if($params['refresh'] !== 14400) { $val = '10m'; foreach(array('d' => 86400, 'h' => 3600, 'm' => 60) as $p => $div) { $res = $params['refresh'] / $div; if($res === intval($res)) { $val = "$res$p"; break; } } $vals[] = $val; } if(count($vals) > 0) { $this->doc .= ' '.join(' ', $vals); } $this->doc .= '}}'; } function table_open($maxcols = null, $numrows = null, $pos = null) { $this->block(); $this->_table = array(); $this->_row = 0; $this->_rowspans = array(); } function table_close($pos = null) { $this->doc .= $this->_table_to_wikitext($this->_table); } function tablerow_open() { $this->block(); $this->_table[++$this->_row] = array(); $this->_key = 1; while(isset($this->_rowspans[$this->_key])) { --$this->_rowspans[$this->_key]; if($this->_rowspans[$this->_key] === 1) { unset($this->_rowspans[$this->_key]); } ++$this->_key; } } function tablerow_close() { $this->block(); } function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { $this->_cellopen('th', $colspan, $align, $rowspan); } function _cellopen($tag, $colspan, $align, $rowspan) { $this->block(); $this->_table[$this->_row][$this->_key] = compact('tag', 'colspan', 'align', 'rowspan'); if($rowspan > 1) { $this->_rowspans[$this->_key] = $rowspan; $this->_ownspan = true; } $this->_pos = strlen($this->doc); } function tableheader_close() { $this->_cellclose(); } function _cellclose() { $this->block(); $this->_table[$this->_row][$this->_key]['text'] = trim(substr($this->doc, $this->_pos)); $this->doc = substr($this->doc, 0, $this->_pos); $this->_key += $this->_table[$this->_row][$this->_key]['colspan']; while(isset($this->_rowspans[$this->_key]) && !$this->_ownspan) { --$this->_rowspans[$this->_key]; if($this->_rowspans[$this->_key] === 1) { unset($this->_rowspans[$this->_key]); } ++$this->_key; } $this->_ownspan = false; } function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { $this->_cellopen('td', $colspan, $align, $rowspan); } function tablecell_close() { $this->_cellclose(); } function plugin($name, $args, $state = '', $match = '') { $this->not_block(); // This will break for plugins which provide a catch-all render method // like the do or pagenavi plugins # $plugin =& plugin_load('syntax',$name); # if($plugin === null || !$plugin->render($this->getFormat(),$this,$args)) { $this->doc .= $match; # } } function _echoLinkTitle($title) { if(is_array($title)) { $this->internalmedia( $title['src'], $title['title'], $title['align'], $title['width'], $title['height'], $title['cache'], $title['linking'] ); } else { $this->doc .= $title; } } /** * Helper for table to wikitext conversion * * @author Adrian Lang * @param array $_table * @return string */ private function _table_to_wikitext($_table) { // Preprocess table for rowspan, make table 0-based. $table = array(); $keys = array_keys($_table); $start = array_pop($keys); foreach($_table as $i => $row) { $inorm = $i - $start; if(!isset($table[$inorm])) $table[$inorm] = array(); $nextkey = 0; foreach($row as $cell) { while(isset($table[$inorm][$nextkey])) { $nextkey++; } $nextkey += $cell['colspan'] - 1; $table[$inorm][$nextkey] = $cell; $rowspan = $cell['rowspan']; $i2 = $inorm + 1; while($rowspan-- > 1) { if(!isset($table[$i2])) $table[$i2] = array(); $nu_cell = $cell; $nu_cell['text'] = ':::'; $nu_cell['rowspan'] = 1; $table[$i2++][$nextkey] = $nu_cell; } } ksort($table[$inorm]); } // Get the max width for every column to do table prettyprinting. $m_width = array(); foreach($table as $row) { foreach($row as $n => $cell) { // Calculate cell width. $diff = (utf8_strlen($cell['text']) + $cell['colspan'] + ($cell['align'] === 'center' ? 3 : 2)); // Calculate current max width. $span = $cell['colspan']; while(--$span >= 0) { if(isset($m_width[$n - $span])) { $diff -= $m_width[$n - $span]; } } if($diff > 0) { // Just add the difference to all cols. while(++$span < $cell['colspan']) { $m_width[$n - $span] = (isset($m_width[$n - $span]) ? $m_width[$n - $span] : 0) + ceil($diff / $cell['colspan']); } } } } // Write the table. $types = array('th' => '^', 'td' => '|'); $str = ''; foreach($table as $row) { $pos = 0; foreach($row as $n => $cell) { $pos += utf8_strlen($cell['text']) + 1; $span = $cell['colspan']; $target = 0; while(--$span >= 0) { if(isset($m_width[$n - $span])) { $target += $m_width[$n - $span]; } } $pad = $target - utf8_strlen($cell['text']); $pos += $pad + ($cell['colspan'] - 1); switch($cell['align']) { case 'right': $lpad = $pad - 1; break; case 'left': case '': $lpad = 1; break; case 'center': $lpad = floor($pad / 2); break; } $str .= $types[$cell['tag']].str_repeat(' ', $lpad). $cell['text'].str_repeat(' ', $pad - $lpad). str_repeat($types[$cell['tag']], $cell['colspan'] - 1); } $str .= $types[$cell['tag']].DOKU_LF; } return $str; } } //Setup VIM: ex: et ts=4 enc=utf-8 :