Re: [cowiki-dev] coWiki definition, new parser
Archie Campbell <[email protected]>
| Newsgroups | gmane.comp.php.cowiki.devel |
|---|---|
| Message-ID | <[email protected]> |
It's not pretty and there are some bleeding edges in this working copy. I've done it like this because I can't stand <1><1></1></1> giving <1><1></1> and hurling the rest back on yourself. There are two bits doing noop, which is why the thing is broken, probably. That was a mistake, actually. Please toy with it. Be merciless with the copy and gentle with me, that's probably the best idea. It may become necessary to permute the WikiReverseParser into something resembling the modified WikiParser. I'll get there once the latter is well behaved. So. I'm off to Scotland for a week; enjoy breaking things. Regards, Archie Archie Campbell wrote: > Guys, > > The WikiParser is trotting along nicely. I'm currently working on > links, to be followed by the toc and then variables and anything > leftover from Paul's specification the other day. > > I'm going on holiday to Scotland for 5 days; from tomorrow evening > until next Sunday, I'll be exclusively real world. I propose to send a > patch from 0.3.4 with the new parser as a work-in-progress. If you > chaps could patch an old copy of Boron and have some fun breaking > things, I'll also include a TEST.IDIOM document with brief > descriptions of any limitations that the code needs, even temporarily. > > The question is... where do I put the patch? And, should I go from > 0.3.4 or CVS HEAD? > > I'm actually quite enjoying the coding, which is nice. It looks > nothing like the old version, and has made strides in definition, I think. > > Regards, > > cmarble > > P.S. Post-AprilFD, would anyone at Google fancy a coWiki? chortle > > Sy wrote: > >>On Apr 2, 2005 2:54 AM, Daniel T. Gorski <[email protected]> wrote: >> >> >> >>>>>I would like to have more features listed that are beyond other wikis and >>>>>make coWiki special, very special :) >>>>> >>>>> >> >>It is necessary to create a good document which describes things more >>technically before the release of the Beauty milestone. We will need >>to distinguish ourselves from other solutions. This document will >>help us understand the coWiki "Character" and make that milestone more >>profound. >> >>This is now on my to do list. =/ >> >>--------------------------------------------------------------------- >>To unsubscribe, e-mail: [email protected] >>For additional commands, e-mail: [email protected] >> >> >> >> >> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
class.WikiParser.php
(text/html, 46.7 KB)
<?php /** * * $Id: class.WikiParser.php,v 1.36 2005/02/14 22:23:53 dgorski Exp $ * * This file is part of coWiki. coWiki is free software under the terms of * the GNU General Public License (GPL). Read the LICENSE file. If you did * not receive a copy of the license and are not able to obtain it through * the internet, please send a note to <[email protected]> so we can mail * you a copy immediately. * * <pre> * Helping hands: Matt Ho <[email protected]> * </pre> * * @package parse * @subpackage class * @access public * * @author Daniel T. Gorski, <[email protected]> * @copyright (C) Daniel T. Gorski, {@link http://www.develnet.org} * @license http://www.gnu.org/licenses/gpl.html * @version $Revision: 1.36 $ * */ /** * coWiki - Wiki parser class * * @package parse * @subpackage class * @access public * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ class WikiParser extends Object { protected static $Instance = null; protected $aToc = array(); protected $aEmph = array(); // -------------------------------------------------------------------- /** * Get instance * * @access public * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ public function getInstance() { if (!self::$Instance) { self::$Instance = new WikiParser; } return self::$Instance; } // -------------------------------------------------------------------- /** * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function __construct() {} // --- Helper methods ------------------------------------------------- /** * Set doc toc * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function setDocToc($sStr) { $this->sDocToc = $sStr; } // -------------------------------------------------------------------- /** * Get doc toc * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function getDocToc() { return $this->sDocToc; } // -------------------------------------------------------------------- /** * Get token count * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function getTokenCount() { return sizeof($this->aToken); } // -------------------------------------------------------------------- /** * Set restore flag * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function setRestoreFlag($bFlag) { $this->bRestoreFlag = $bFlag; } // -------------------------------------------------------------------- /** * Get restore flag * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function getRestoreFlag() { return $this->bRestoreFlag; } // -------------------------------------------------------------------- // --- Element creators ----------------------------------------------- /** * Create a <noop>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createNoopElement(&$sStr) { return '<noop>'.$sStr.'</noop>'; } // -------------------------------------------------------------------- /** * Create a <rem>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createRemarkElement(&$sStr) { return '<rem>'.$sStr.'</rem>'; } // -------------------------------------------------------------------- /** * Create a <<pre>>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createPreElement(&$sStr) { return "<pre>\r".str_replace("\n", "\r", $sStr)."\r</pre>"; } // -------------------------------------------------------------------- /** * Create a <<code>>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createCodeElement(&$sStr) { return "<code>\r".str_replace("\n", "\r", $sStr)."\r</code>"; } // -------------------------------------------------------------------- /** * Create a <posting>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createPostingElement(&$sStr) { return "<posting>\r".str_replace("\n", "\r", $sStr)."\r</posting>"; } // -------------------------------------------------------------------- /** * Create a simple <plugin>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createSimplePluginElement(&$sStr) { return '<plugin name="'.$sStr.'"/>'; } // -------------------------------------------------------------------- /** * Create a parameterized <plugin>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createParamPluginElement(&$sStr, &$sAttr) { if (trim($sAttr) == '') { return $this->createSimplePluginElement($sStr); } return '<plugin name="'.$sStr.'" '.trim(unescape($sAttr)).'/>'; } // -------------------------------------------------------------------- /** * Create a <h>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createHeadingElement(&$sStr, &$sLevel) { return '<h'.$sLevel.'>'.$sStr.'</h'.$sLevel.'>'; } // -------------------------------------------------------------------- /** * Create a <link>-element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createLinkElement($sStr) { $aStr = explode(')(', $sStr); $sStr = '<link strref="'; $sStr .= str_replace('|','¦',trim($this->restoreTokens($aStr[0]))); $sStr .= '">'; $sStr .= trim($this->restoreTokens($aStr[0])); $sStr .= '</link>'; return $sStr; } // -------------------------------------------------------------------- /** * Create uri element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createUriElement(&$sStr) { $sStr = '<uri strref="'.$sStr.'"/>'; return $sStr; } // -------------------------------------------------------------------- /** * Create toc element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createTocElement() { // Setting $i = 0 leads to undesired repetition of the first // "title" element in a bullet list // The default behaviour of <toc> is not to display the first // level $i = 1; $sStr = ''; if (sizeof($this->aToc) > 0) { $sStr = $this->buildToc($i, $this->aToc[$i]['DEPTH']); if ($sStr != '') { $sStr = '<ul>' . $sStr . '</ul>'; } } return '<toc>'.$this->restoreTokens($sStr).'</toc>'; } // -------------------------------------------------------------------- /** * Create rule element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createRuleElement() { return '<hr/>'; } // -------------------------------------------------------------------- /** * Create simple table element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createSimpleTableElement(&$sStr) { return '<table>' .$this->buildTable($sStr) .'</table>'; } // -------------------------------------------------------------------- /** * Create param table element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createParamTableElement(&$sStr, &$sAttr) { if (trim($sAttr) == '') { return $this->createSimpleTableElement($sStr); } return '<table '.trim(unescape($sAttr)).'>' .$this->buildTable($sStr) .'</table>'; } // -------------------------------------------------------------------- /** * Create quote element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createQuoteElement(&$sStr) { return '<q>'.$this->invokeParagraphs($sStr).'</q>'; } // -------------------------------------------------------------------- /** * Create var element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createVarElement(&$sStr, &$sName) { return '<var name="'.$sName.'"/>'; } // -------------------------------------------------------------------- /** * Create break element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createBreakElement() { return '<br/>'; } // -------------------------------------------------------------------- /** * Create sub element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.3 */ protected function createSubElement(&$sStr) { return '<sub>'.$sStr.'</sub>'; } // -------------------------------------------------------------------- /** * Create sup element * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.3 */ protected function createSupElement(&$sStr) { return '<sup>'.$sStr.'</sup>'; } // -------------------------------------------------------------------- protected function createJustificationElement(&$sStr, &$mMeta) { return '<'.$mMeta.'>'.$this->invokeParagraphs($sStr).'</'.$mMeta.'>'; } protected function createEmphasisElement(&$sStr, &$mMeta) { $sOpen = $sClose = ''; for ($i=0 ; $i < strlen($mMeta) ; $i++ ){ switch(substr($mMeta,$i,1)){ case '_': $sOpen .= "<u>"; $sClose = "</u>".$sClose; break; case '-': if(!strcmp(substr($mMeta,$i+1,1),'-')){ $sOpen .= "<strike>"; $sClose = "</strike>".$sClose; $i++; } break; case '=': $sOpen .= "<tt>"; $sClose = "</tt>".$sClose; break; case '*': $sOpen .= "<b>"; $sClose = "</b>".$sClose; break; case '/': $sOpen .= "<i>"; $sClose = "</i>".$sClose; break; } } return $sOpen . $sStr . $sClose; } // --- Element creator helpers ---------------------------------------- /** * Generate "toc" (Document Table of Contents) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function buildToc(&$i, &$nDepth) { $sStr = ''; // Iterate though all toc-entries while (isset($this->aToc[$i]['DEPTH']) && $this->aToc[$i]['DEPTH'] == $nDepth) { $sStr .= '<li>'; $sStr .= '<link topicref="'.($i+1).'">'.$sAlias.'</link>'; $i++; // Recurse (indent) if toc-entry is nested if (isset($this->aToc[$i]['DEPTH'])) { if ($this->aToc[$i]['DEPTH'] > $nDepth) { $sStr .= '<ul>'; $sStr .= $this->buildToc($i, $this->aToc[$i]['DEPTH']); $sStr .= '</ul>'; } } $sStr .= '</li>'; } return $sStr; } // -------------------------------------------------------------------- /** * Catch paragraphs * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function invokeParagraphs(&$sStr) { // Remove multiple newlines $sStr = preg_replace('=\n+=', "\n", $sStr); // Insert paragraphs $sStr = preg_replace('=\n([^\r]+)$=Um', '<p>\1</p>', $sStr); return $sStr; } // -------------------------------------------------------------------- /** * All strings passed to this method MUST NOT contain any "<" or ">". * Treat this string with "htmlentities()" or a similar function first. * * @access public * @param string The wiki source string * @return string Wiki XML * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ public function parse($sStr) { $sRet = ''; $sContent = ''; $bList = false; $bTable = false; $bGreedy = false; $aList = array(); $aTable = array(); $aRows; // {{{ DEBUG }}} Logger::info('Start parsing wiki document.'); // --- $sStr = trim(escape($sStr)); // Replace possible tabulators $sStr = str_replace("\t", ' ', $sStr); // Replace possible \r\n or \n\r with \n $sStr = str_replace("\r\n", "\n", $sStr); $sStr = str_replace("\n\r", "\n", $sStr); $aRows = explode( "\n",$sStr."\n" ); $i = 0; $n = sizeof( $aRows ); $sRow = $aRows[$i]."\n"; $t = 0; $l = -1; //table & list while( true ) { $sContent = ''; $aMatches = array(); //table of contents alone ^<toc/>$ if( preg_match( '=^<toc(/?)>$=', $sRow ) ) { $sRet .= "\n\t\t\n"; //end of table } else if( $t > 0 && preg_match( '=^</table>=i', $sRow, $aMatches )) { $sParam = trim($aTable[$t][0]); if( $sParam == '') { $sRet .= "\n".'<table>'.$this->buildTable($aTable[$t]). '</table>'."\n"; } else { $sRet .= "\n".'<table '.trim(unescape($sParam)).'>'. $this->buildTable($aTable[$t]).'</table>'."\n"; } if( --$t == 0 ){ $bTable = false; } //beginning of table } else if( preg_match( '=^<table([^>]*)>$=i', $sRow, $aMatches )) { $bTable = true; $aTable[++$t] = array(); array_push( $aTable[$t], $aMatches[1] ); //beginning of list } else if( preg_match( '=^([\s*#]{0,}[*#]) ([^\n]*)$=', $sRow, $aMatches )) { $bList = true; $aList[++$l] = array(); $aList[$l]['TEXT'] = $this->processContent( $aMatches[2] ); $aList[$l]['DEPTH'] = strlen( $aMatches[1] ); $aList[$l]['TYPE'] = (substr($aMatches[1],-1)=='*'? 'ul':'ol'); //pre code posting q start at BOL, are greedy until // closing tag anywhere on a line } else if (preg_match( '=^<(pre|code|posting|q)>=i', $sRow, $aMatch ) ) { $sRow = substr( $sRow, strlen($aMatch[0]) ); do { if (preg_match( '=(.*)</'.$aMatch[1].'>=i', $sRow, $aMatches )) { $sRet .= '<'.$aMatch[1].'>'.$sContent . $aMatches[1].'</'.$aMatch[1].'>'; break; } else { $sContent .= $sRow; } if( ++$i < $n ){ $sRow = $aRows[$i]; }else{ break; } } while(true); if( $i == $n ) { $sRet .= '<'.$aMatch[1].'>'. $sContent . '</'.$aMatch[1].'>'; break; } //horizontal rule } else if (preg_match( '=^-{3,}(\s|$)=', $sRow, $aMatches ) ) { $sRet .= '<hr/>'."\n"; //noop } else if (preg_match( '=((.*)<noop>)=i', $sRow, $aMatches )) { $sContent = $this->processContent( $aMatches[2], false ) . '<noop>'; $sRow = substr( $sRow, strlen($aMatches[1]) ); do { if (preg_match( '=(.*)</noop>=' , $sRow, $aMatches )) { $sContent .= $aMatches[1] . '</noop>'; break; } else { $sContent .= $sRow; } if(++$i<$n){ $sRow = $aRows[$i]; } else { break; } } while(true); $sRet .= $sContent; } else { //end of list if($bList) { $d = 0; $sListType = $aList[$d]['TYPE']; $nDepth = $aList[$d]['DEPTH']; $sContent .= "\n".'<list>'. '<'.$sListType.'>'. $this->buildList($d,$nDepth,$aList). '</'.$sListType.'>'. '</list>'."\n"; $bList = false; $aList = array(); $l = -1; if($bTable){ array_push( $aTable[$t], $sContent ); array_push( $aTable[$t], $sRow ); }else{ $sRet .= $sContent; $sRet .= $this->processContent( $sRow ); } //more table }else if($bTable) { array_push( $aTable[$t], $sRow ); //non table/list row }else{ $sContent = trim($this->processContent( $sRow )); if( $sContent != '' ){ $sRet .= '<p>'.$sContent.'</p>'."\n"; } } } if(sizeof($aMatches)) { $sRow = substr( $sRow, strlen( $aMatches[0] ) ); } else if ( ++$i < $n ) { $sRow = $aRows[$i]."\n"; } else { break; } } if( preg_match( '=^(.*)\n\t\t\n(.*)$=s', $sRet, $aMatches )) { $t = 1; if (sizeof($this->aToc) > 0) { $sToc = $this->buildToc($t, $this->aToc[$t]['DEPTH']); if ($sToc != '') { $sToc = '<ul>' . $sToc . '</ul>'; } } return $aMatches[1] . $sToc . $aMatches[2]; } // {{{ DEBUG }}} Logger::info('Finished parsing wiki document.'); return $sRet; } protected function resetEmphasis() { $this->$aEmph = array( "b" => false, "u" => false, "i" => false, "f" => false, "s" => false, "j" => '', "p" => '' ); } protected function processContent( $sStr, $bClose = true ) { $sRet = ''; $aMatches = array(); if ($bClose) { $this->resetEmphasis(); } $sStr = ltrim( $sStr ); while( strlen( $sStr ) > 0 ) { if ($sStr == "\n"){ break; } //noop if (preg_match( '=^<noop>(.*)</noop>=is', $sStr, $aMatches )) { $sRet .= '<noop>' . $aMatches[1] . '</noop>'; //links (()()) & [[][]] }else if( preg_match( '=^\(\(([^\(\)]+|[^\(\)]+' . '\([^\(\)]+\)[^\(\)]*|' . '[^\(\)]+\)\([^\(\)]+)\)\)=Usx', $sStr, $aMatches )) { $aLink = explode( ')(', $aMatches[1] ); $aLink[0] = str_replace( '|', '¦', $aLink[0] ); $sRet .= '<link strref="' . trim($aLink[0]) . '">'; if ( isset( $aLink[1]) ) { $sRet .= trim($aLink[1]); } else { $sRet .= trim($aLink[0]); } $sRet .= '</link>'; //WikiWords }else if( preg_match( '=^([A-Z][a-z]+([A-Z][a-z]+)+)=s', $sStr, $aMatches )) { $sLink = trim(preg_replace( '=([A-Z])=', ' \1', $aMatches[1] )); $sRet .= '<link strref="' . $sLink . '">' . $sLink . '</link>'; //URIs }else if( preg_match( '=^(http://|https://|ftp://|mailto:|news:) ([-_A-Z0-9\S]*) (\*\s|\=\s|"|<|>|<|>|\(|\)|\s|$)?? =six', $sStr, $aMatches )) { $sRet .= '<uri strref="' . $aMatches[1].$aMatches[2] . '"/>'; //headings }else if( preg_match( '=^(\+{1,})\s*(.*)$=s', $sStr, $aMatches )) { $sContent = $this->processContent($aMatches[2]); if( preg_match( '=<link\s+strref\="([^>]+)"> ([^<]*)</link>=USx', $sContent, $aLinkMatches ) ) { $sAlias = !empty($aLinkMatches[2]) ? $aLinkMatches[2] : $aLinkMatches[1] ; } else { $sAlias = $sContent; } $i = sizeof($this->aToc); $this->aToc[$i]['TEXT'] = $sAlias; $this->aToc[$i]['DEPTH'] = strlen( $aMatches[1] ); $sTag = 'h'.strlen($aMatches[1]); $sRet .= '<'. $sTag .'>'. $sContent . '</'. $sTag .'>'; //breaks //justification }else if( preg_match( '=^<(/??)(left|center|right)>=is', $sStr, $aMatches )) { if( $aMatches[1] == '/' && $this->aEmph['j'] == $aMatches[2]{0} ) { $sRet .= '</'. $aMatches[2] .'>'; $this->aEmph['j'] = ''; } else if ( $aMatches[1] == '' && $this->aEmph['j'] == '' ) { $sRet .= '<'. $aMatches[2] .'>'; $this->aEmph['j'] = $aMatches[2]{0}; } //subscript, superscript }else if( preg_match( '=^<(/??)(sub|sup)>=is', $sStr, $aMatches ) ) { if( $aMatches[1] == '/' && $this->aEmph['p'] == $aMatches[2] ) { $sRet .= '</' . $aMatches[2] . '>'; $this->aEmph['p'] = ''; }else if ( $aMatches[1] == '' && $this->aEmph['p'] == '' ) { $sRet .= '<' . $aMatches[2] . '>'; $this->aEmph['p'] = $aMatches[2]; } //emphasis /* }else if( preg_match( '#^([^-=\*_/]*?) ([-=\*_/]{2,}) ([^-=\*_/]*?)#x', */ }else if( preg_match( '#^([-=\*_/]{2,})#', $sStr, $aMatches ) ) { while( strlen( $aMatches[1] ) ) { if( strlen( $aMatches[1] ) >= 2 && $aMatches[1]{0} == $aMatches[1]{1} ) { if( strlen( $aMatches[1] ) > 3 && $aMatches[1]{1} == $aMatches[1]{2} && $aMatches[1]{3} == $aMatches[1]{2} ) { $sRet .= str_repeat( $aMatches[1]{0}, 2 ); $aMatches[1] = substr( $aMatches[1], 4 ); continue; } $sEmph = ''; switch ( $aMatches[1]{0} ) { case '-': $sEmph=($this->aEmph["s"]?'/':'').'strike'; $this->aEmph["s"] = !$this->aEmph["s"]; break; case '=': $sEmph=($this->aEmph["f"]?'/':'').'tt'; $this->aEmph["f"] = !$this->aEmph["f"]; break; case '*': $sEmph=($this->aEmph["b"]?'/':'').'b'; $this->aEmph["b"] = !$this->aEmph["b"]; break; case '_': $sEmph=($this->aEmph["u"]?'/':'').'u'; $this->aEmph["u"] = !$this->aEmph["u"]; break; case '/': $sEmph=($this->aEmph["i"]?'/':'').'i'; $this->aEmph["i"] = !$this->aEmph["i"]; break; } $sRet .= '<'.$sEmph.'>'; $aMatches[1] = substr( $aMatches[1], 2 ); } else { $sRet .= $aMatches[1]{0}; $aMatches[1] = substr( $aMatches[1], 1 ); } } }else if( preg_match( '#^((([-=\*_/]{1})(?!\3)|\s|[A-Za-z0-9])*)#x', $sStr, $aMatches ) ) { $sRet .= $aMatches[0]; } if( isset($aMatches[0]) ) { $sStr = substr( $sStr, strlen($aMatches[0]) ); }else{ break; } } if($bClose) { if( $this->aEmph['s'] ) { $sRet .= '</strike>'; } if( $this->aEmph['f'] ) { $sRet .= '</tt>'; } if( $this->aEmph['b'] ) { $sRet .= '</b>'; } if( $this->aEmph['u'] ) { $sRet .= '</u>'; } if( $this->aEmph['i'] ) { $sRet .= '</i>'; } if( $this->aEmph['p'] != '' ) { $sRet .= '</' . $this->aEmph['p'] .'>'; } $this->resetEmphasis(); } /* if( $this->aEmph['j'] != '' ) { switch( $this->aEmph['j'] ) { case 'l': $sRet .= '</left>';break; case 'r': $sRet .= '</right>';break; case 'c': $sRet .= '</center>';break; } } */ return $sRet; } /** * &build table * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function buildTable( &$aTable ) { $sRet = ''; $sContent = ''; $sRow = ''; $bPriorRow = false; $j = 1; $v = sizeof($aTable); while( ++$j < $v ) { /* if (!strlen(trim($aTable[$j]))) { return '<tr valign="top"><td colspan="1">'.$sStr.'</td></tr>'; } */ //rows expected to hold table syntax (pipes), may instead //make room for ongoing content (tables, lists, etc) $sRow = $aTable[$j]; //find table syntax within a (long?) row while( strlen($sRow) && preg_match( '=^([\|!][-+]?)( {0,1}[^\|!\n]*)?=', $sRow, $aMatches) ) { //found exploded row begins with pipe syntax, poss params $sRow = substr( $sRow, strlen($aMatches[0]) ); $nType = 0; //tables made of cells, headers, rows, caption if ($aMatches[1]=="|") { $sRet .= '<td'; $nType = 1; }else if ($aMatches[1]=="!") { $sRet .= '<th'; $nType = 2; }else if ($aMatches[1]=="|-") { if ($bPriorRow) { $sRet .= "</tr>\n<tr"; } else { $sRet .= '<tr'; } $nType = 3; $bPriorRow = true; }else if ($aMatches[1]=="|+") { $sRet .= '<caption>'.trim($aMatches[2])."</caption>\n"; continue 1; } //content can be large, room for it here $sContent = ''; if ($aMatches[2]{0}==' ') { //left a space to denote no params $sRet .= '>'; $sContent .= substr($aMatches[2],1); //expect double pipe, bang or newline } elseif (strlen($aMatches[2])) { //got params. look for pipe syntax that blocked last preg $sRet .= ' '.unescape($aMatches[2]).'>'; if (preg_match('=^\|([^\|!\n]*)=',$sRow,$aMatches)) { //got rest of pipe syntax (...|text) $sContent .= $aMatches[1]; $sRow = substr( $sRow, strlen($aMatches[0]) ); } } else { $sRet .= '>'; } //look ahead now. if ($sRow == "\n") { //look ahead to close our content while( ++$j<$v ){ $sRow = $aTable[$j]; //pipe syntax (will force $sContent) to continue if ($sRow{0} == '|' || $sRow{0} == '!') { $sRet .= $this->processContent($sContent); switch($nType){ case 1: $sRet .= "</td>\n"; break; case 2: $sRet .= "</th>\n"; break; } --$j; continue 3; } else { //non-pipe (ever-increasing) content $sContent .= $sRow; } } $j = $v; $sRet .= $this->processContent($sContent); switch($nType){ case 1: $sRet .= "</td>\n"; break; case 2: $sRet .= "</th>\n"; break; } continue 2; } elseif ( ( $sRow{0} == "|" || $sRow{0} == "!") && ($sRow{1} == "|" || $sRow{1} == "!") ) { $sRow = substr($sRow, 1); $sRet .= $this->processContent($sContent); switch($nType){ case 1: $sRet .= "</td>\n"; break; case 2: $sRet .= "</th>\n"; break; } continue 1; } } } if( $bPriorRow ) { return $sRet."</tr>"; } else { return $sRet; } } protected function buildList(&$i, &$nDepth, &$aList) { $sStr = ''; // Iterate though all list-entries while (isset($aList[$i]['DEPTH']) && $aList[$i]['DEPTH'] == $nDepth) { $sStr .= '<li>'; $sStr .= $aList[$i]['TEXT']; $i++; // Recurse (indent) if list-entry is nested if (isset($aList[$i]['DEPTH'])) { if ($aList[$i]['DEPTH'] > $nDepth) { // Correct items that are intended too deep $aList[$i]['DEPTH'] = $nDepth + 1; $sListType = $aList[$i]['TYPE']; $sStr .= '<'.$sListType.'>'; $sStr .= $this->buildList( $i, $aList[$i]['DEPTH'], $aList ); $sStr .= '</'.$sListType.'>'; } } $sStr .= '</li>'; } return $sStr; } // -------------------------------------------------------------------- /** * Treat NOOP (no operation) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processNoOperation(&$sStr) { // Treat <noop> ... </noop> // This tag can not be nested. $sStr = preg_replace_callback( '=<noop>(.+)</noop>=Usi', array(&$this, 'tokenizeNoOperation'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Treat REMARKS * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processRemark(&$sStr) { // Treat <rem> ... </rem> // This tag can not be nested. $sStr = preg_replace_callback( '=<rem>(.+)</rem>=Usi', array(&$this, 'tokenizeRemark'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Treat preformatted PRE * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processPre(&$sStr) { // Treat <pre> ... </pre> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<pre>\n(.+)\n</pre>(\s|$)=Umsi', array(&$this, 'tokenizePre'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Treat preformatted CODE * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processCode(&$sStr) { // Treat <code> ... </code> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<code>\n(.+)\n</code>(\s|$)=Umsi', array(&$this, 'tokenizeCode'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Treat POSTING * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processPosting(&$sStr) { // Treat <posting> ... </posting> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<posting>\n(.+)\n</posting>(\s|$)=Umsi', array(&$this, 'tokenizePosting'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * ((foo)), ((foo)(bar)) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processLink(&$sStr) { // Treat square-bracket-links ((foo)), ((foo)(bar)) // These tags can not be nested. $sPattern = '=\(\(([^\(\)]+|[^\(\)]+\([^\(\)]+\)[^\(\)]*|' . '[^\(\)]+\)\([^\(\)]+)\)\)=U'; $sStr = preg_replace_callback( $sPattern, array(&$this, 'tokenizeLink'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Convert WikiWords * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processWikiWord(&$sStr) { // Treat WikiWordLinks $sStr = preg_replace_callback( '=([ \n\[\{\']|")([A-Z][a-z]+([A-Z][a-z]+)+)=', array(&$this, 'tokenizeWikiWord'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Automatically clickable URIs * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processClickable(&$sStr) { // Treat clickable URIs (http:, mailto: etc.) $sStr = preg_replace_callback( '= (http://|https://|ftp://|mailto:|news:) (\S+) (\*\s|\=\s|"|<|>|<|>|\(|\)|\s|$) =Usmix', array(&$this, 'tokenizeClickable'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * <toc ...> * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processToc(&$sStr) { // Treat <toc ...> $sStr = preg_replace_callback( '=\n<toc>=Usi', array(&$this, 'tokenizeToc'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * <plugin ...> * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processSimplePlugin(&$sStr) { // Treat <plugin ...> $sStr = preg_replace_callback( '=<plugin +([a-z0-9_.]+)>=Usi', array(&$this, 'tokenizeSimplePlugin'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * <plugin ... parameters> * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processParamPlugin(&$sStr) { // Treat <plugin ... parameters> $sStr = preg_replace_callback( '=<plugin +([a-z0-9_.]+) +(.*)>=Usi', array(&$this, 'tokenizeParamPlugin'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Emphasis: bold, italic, monospaced ... * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processEmphasis(&$sStr) { /* emphasis markup may be as widely spaced as an entire paragraph /x/ - italics *x* - bold --x-- - strikethrough _x_ - underlined =x= - monospaced */ $sEmph= '(-{2}|[_=\*/]+)'; $sCont= '([^\n_=*/]*)'; $sWord= '([^ \n/](\S?|.*(\S|\t)))'; $sStr = $this->collapseEmphasis( $sStr ); $sStr = preg_replace_callback( '~ ((-{2}|[_=*/])+) (([^\n_=*/])+) ((-{2}|[_=*/])+) ~mix', array(&$this,'tokenizeEmphasis'), $sStr ); return $sStr; } protected function collapseEmphasis(&$sStr) { //italic $sStr = preg_replace( '=<(em|i)>(.*)</\1>=ms', '/\2/', $sStr ); //bold $sStr = preg_replace( '=<(strong|b)>(.*)</\1>=ms', '*\2*', $sStr ); //monospaced $sStr = preg_replace( '=<tt>(.*)</tt>=ms', '=\1=', $sStr ); //strikethrough $sStr = preg_replace( '=<strike>(.*)</strike>=ms', '--\1--', $sStr ); //underlined $sStr = preg_replace( '=<u>(.*)</u>=ms', '_\1_', $sStr ); return $sStr; } /** * (Again) treat %VARIABLES%, that may occur in the document. * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processVariable(&$sStr) { $sStr = preg_replace_callback( '=%([A-Z0-9_]+)%=U', array(&$this, 'tokenizeVariable'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Headings (+ -> H1, ++ -> H2, etc.) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processHeading(&$sStr) { // Treat headings (+, ++, +++ etc); // Each tag will be recognized at the begin of a line only! $sStr = preg_replace_callback( '=^(\+{1,6}) ([^\r\n]+)=m', array(&$this, 'tokenizeHeading'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Headings (+ -> H1, ++ -> H2, etc.) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processHorizontalRule(&$sStr) { // Treat horizontal rules (---) $sStr = preg_replace_callback( '=^-{3,}(\s|$)=m', array(&$this, 'tokenizeHorizontalRule'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Breaks (<br>) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processBreak(&$sStr) { // Treat a break $sStr = preg_replace_callback( '=<br>=Ui', array(&$this, 'tokenizeBreak'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Subs (<sub>) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.3 */ protected function processSub(&$sStr) { // Treat a <sub> $sStr = preg_replace_callback( '=<sub>(.*)</sub>=Ui', array(&$this, 'tokenizeSub'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Sups (<sup>) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.3 */ protected function processSup(&$sStr) { // Treat a <sup> $sStr = preg_replace_callback( '=<sup>(.*)</sup>=Ui', array(&$this, 'tokenizeSup'), $sStr ); return $sStr; } // -------------------------------------------------------------------- protected function processJustification(&$sStr) { $sStr = preg_replace_callback( '=<(left|center|right)>(.*)</\1>=Usi', array(&$this, 'tokenizeJustification'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Ordered and unordered bullet lists (Asterisk *, Hash #) * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processList(&$sStr) { // Treat lists $sStr = preg_replace_callback( /* '=\n(( {0,})(\*|#)+ (.*))$=Us',*/ '=\n((\*|#) (.*)\n)(?! {0,}(\*|#))=Us', array(&$this, 'tokenizeList'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Create list * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function createList(&$sStr, &$mMeta) { // Fix multiple bullets at the beginning of the line. // E.g. fix sick things like this to make it intuitive: // // * Item 1 <--> * Item 1 // * Item 2 <--> * Item 2 // ** Item 3 <--> * Item 3 // *** Item 4 <--> * Item 4 // **** Item 5 <--> * Item 5 // ** Item 6 <--> * Item 6 $aArr = explode("\n", $sStr); for ($i=0, $n=sizeof($aArr); $i<$n; $i++) { $aArr[$i] = preg_replace_callback( '=^((\*|#)*)(\2) (.*)$=', array($this, 'createListMultiBulletHelper'), $aArr[$i] ); } $sStr = join("\n", $aArr); preg_match_all( '=^( {0,})(\*|#)+ (.*)$=Ums', $sStr, $aList ); $this->aList = array(); for ($i=0; $i<sizeof($aList[1]); $i++) { //$this->aList[$i]['TEXT'] = $this->restoreTokens($mMeta); //$this->aList[$i]['TEXT'] = $aList[3][$i]; $sEmp = $this->restoreTokens($this->processEmphasis($aList[3][$i])); $this->aList[$i]['TEXT'] = $sEmp; $this->aList[$i]['DEPTH'] = strspn($aList[1][$i], ' '); $this->aList[$i]['TYPE'] = $aList[2][$i]; } $i = 0; $nDepth = $this->aList[$i]['DEPTH']; // Determine the type of the first list element: // Ordered or unordered? $sListType = ($aList[2][0] == '*') ? 'ul' : 'ol'; return '<list>' .'<'.$sListType.'>' .$this->buildList($i, $nDepth) .'</'.$sListType.'>' .'</list>'; } private function createListMultiBulletHelper(&$aMatches) { return str_repeat(' ', strlen($aMatches[1])) .$aMatches[3].' '.$aMatches[4]; } // -------------------------------------------------------------------- /** * Build list * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ /** * Treat TABLE * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processSimpleTable(&$sStr) { // Treat <table> ... </table> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<table>\n(.+)\n</table>(\s|$)=Umsi', array(&$this, 'tokenizeTable'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Process param table * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processParamTable(&$sStr) { // Treat <table> ... </table> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<table +([^>]*)>\n(.+)\n</table>(\s|$)=Umsi', array(&$this, 'tokenizeParamTable'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Treat (block-)quote * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function processQuote(&$sStr) { // Treat <q> ... </q> // Each tag will be recognized at the begin of a line only! // This tag can not be nested. $sStr = preg_replace_callback( '=^<q>(.+)\n+</q>(\s|$)=Umsi', array(&$this, 'tokenizeQuote'), $sStr ); return $sStr; } // -------------------------------------------------------------------- /** * Collect referenced nodes to where the document is linking to * * @access protected * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ protected function addReferencedNode($Obj) { $this->RefNodes->add($Obj); } // -------------------------------------------------------------------- /** * Get referenced nodes * * @access public * * @author Daniel T. Gorski, <[email protected]> * @since coWiki 0.3.0 */ public function getReferencedNodes() { return $this->RefNodes; } } // of class /* Prospero: That cross you wear around your neck; is it only a decoration, or are you a true Christian believer? Francesca: Yes, I believe - truly. Prospero: Then I want you to remove it at once! - and never to wear it within this castle again! Do you know how a falcon is trained my dear? Her eyes are sown shut. Blinded temporarily she suffers the whims of her God patiently, until her will is submerged and she learns to serve - as your God taught and blinded you with crosses. Francesca: You had me take off my cross because it offended ... Prospero: It offended no-one. No - it simply appears to me to be discourteous to ... to wear the symbol of a deity long dead. My ancestors tried to find it. And to open the door that seperates us from our Creator. Francesca: But you need no doors to find God. If you believe ... Prospero: Believe?! If you believe you are gullible. Can you look around this world and believe in the goodness of a god who rules it? Famine, Pestilence, War, Disease and Death! They rule this world. Francesca: There is also love and life and hope. Prospero: Very little hope I assure you. No. If a god of love and life ever did exist ... he is long since dead. Someone ... something rules in his place. */ ?>