CVS update: /cowiki/includes/cowiki/class/parse/

[email protected] 29 May 2005 15:39:38 -0000
Newsgroups gmane.comp.php.cowiki.cvs
Message-ID <[email protected]>
User: cmarble 
Date: 2005/05/29 08:39:38

Added:
   cowiki/includes/cowiki/class/parse/class.TestWikiParser.php

Modified:
   cowiki/includes/cowiki/class/parse/class.CoWikiParser.php
   cowiki/includes/cowiki/class/parse/class.CoWikiReverseParser.php
   cowiki/includes/cowiki/class/parse/class.WikiParser.php
   cowiki/includes/cowiki/class/parse/class.WikiReverseParser.php

Log:
 Issue number:  152,174,195,198,208,196
 Submitted by: cmarble 
 
 New parser objects. Comprehensive test suite in misc/development/tests.

File Changes:

Directory: /cowiki/includes/cowiki/class/parse/
===============================================

File [changed]: class.CoWikiParser.php
Url: http://cowiki.tigris.org/source/browse/cowiki/includes/cowiki/class/parse/class.CoWikiParser.php?r1=1.13&r2=1.14
Delta lines:  +30 -27
---------------------
--- class.CoWikiParser.php	16 Jan 2005 23:25:09 -0000	1.13
+++ class.CoWikiParser.php	29 May 2005 15:39:35 -0000	1.14
@@ -2,7 +2,7 @@
 
 /**
  *
- * $Id: class.CoWikiParser.php,v 1.13 2005/01/16 23:25:09 dgorski Exp $
+ * $Id: class.CoWikiParser.php,v 1.14 2005/05/29 15:39:35 cmarble 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
@@ -17,7 +17,7 @@
  * @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.13 $
+ * @version     $Revision: 1.14 $
  *
  */
 
@@ -30,6 +30,8 @@
  *
  * @author      Daniel T. Gorski, <[email protected]>
  * @since       coWiki 0.3.0
+ * @author      Archie Campbell <[email protected]>
+ * @since       coWiki 0.4.0
  *
  * @todo        [D11N]  Complete documentation
  */
@@ -53,18 +55,6 @@
         return self::$Instance;
     }
 
-    /**
-     * Create link element
-     *
-     * @access  protected
-     * @param   string
-     * @return  void
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
     protected function __construct() {}
 
     // --- Element creators -----------------------------------------------
@@ -78,10 +68,12 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function createLinkElement($sStr) {
+    protected function createLinkElement($sStr, $bSquare = false) {
 
-        $aStr = explode(')(', $sStr);
+        $aStr = explode(($bSquare?"][":")("),$sStr);
 
         $sRef   = false;
         $sAlias = false;
@@ -98,7 +90,7 @@
         $sPattern = '^(http://|https://|ftp://|mailto:|news:).*';
 
         if (preg_match('=' . $sPattern . '=', $sRef)) {
-            return $this->createUriLinkElement($sRef, $sAlias);
+            return $this->createUriLinkElement($sRef, $sAlias, $bSquare);
         }
 
         // Obviously the reference string is not an URI. Check if it has
@@ -109,12 +101,12 @@
         if (sizeof($aWebRef) > 1) {
             $sWeb = trim($aWebRef[0]);
             $sRef = trim($aWebRef[1]);
-            return $this->createGlobalLinkElement($sWeb, $sRef, $sAlias);
+            return $this->createGlobalLinkElement($sWeb, $sRef, $sAlias, $bSquare);
         }
 
         // The reference string was not an URI and it has no reference
         // to an other web -> Look up in database.
-        return $this->createLocalLinkElement($sRef, $sAlias);
+        return $this->createLocalLinkElement($sRef, $sAlias, $bSquare);
     }
 
     /**
@@ -127,9 +119,12 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function createUriLinkElement($sRef, $sAlias) {
-        $sStr =  '<link href="'. $sRef . '">';
+    protected function createUriLinkElement($sRef, $sAlias, $bSquare) {
+        $sStr =  '<link origin='.($bSquare?'"square"':'"round"').
+			' href="'. $sRef . '">';
 
         if ($sAlias && $sAlias != '') {
             $sStr .= $sAlias ;
@@ -151,8 +146,10 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function createGlobalLinkElement($sWeb, $sRef, $sAlias) {
+    protected function createGlobalLinkElement($sWeb, $sRef, $sAlias, $bSquare) {
         $Context = RuntimeContext::getInstance();
 
         $sWebStr = unescape($sWeb);
@@ -188,14 +185,16 @@
                 $this->addReferencedNode($Node);
 
                 // Create element
-                $sStr = '<link idref="' . $Node->get('id') . '">';
+                $sStr = '<link origin='.($bSquare?'"square"':'"round"').
+				' idref="' . $Node->get('id') . '">';
                 $sStr .=    $sAlias;
                 $sStr .= '</link>';
             }
         }
 
         if (!is_object($WebNode) || !is_object($Node)) {
-            $sStr = '<link strref="' . $sWeb . '¦' . $sRef . '">';
+            $sStr = '<link origin='.($bSquare?'"square"':'"round"').
+			    ' strref="' . $sWeb . '¦' . $sRef . '">';
             $sStr .=    $sAlias;
             $sStr .= '</link>';
         }
@@ -213,8 +212,10 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function createLocalLinkElement($sRef, $sAlias) {
+    protected function createLocalLinkElement($sRef, $sAlias, $bSquare) {
         $Context = RuntimeContext::getInstance();
 
         // Get current directory/document object
@@ -236,13 +237,15 @@
             $this->addReferencedNode($NewNode);
 
             // Create element
-            $sStr = '<link idref="' . $NewNode->get('id') . '">';
+            $sStr = '<link origin='.($bSquare?'"square"':'"round"').
+			    ' idref="' . $NewNode->get('id') . '">';
             $sStr .=    $sAlias;
             $sStr .= '</link>';
         }
 
         if (!is_object($NewNode)) {
-            $sStr = '<link strref="' . $sRef . '">';
+            $sStr = '<link origin='.($bSquare?'"square"':'"round"').
+			    ' strref="' . $sRef . '">';
             $sStr .=    $sAlias;
             $sStr .= '</link>';
         }

File [changed]: class.CoWikiReverseParser.php
Url: http://cowiki.tigris.org/source/browse/cowiki/includes/cowiki/class/parse/class.CoWikiReverseParser.php?r1=1.15&r2=1.16
Delta lines:  +61 -12
---------------------
--- class.CoWikiReverseParser.php	16 Jan 2005 23:25:09 -0000	1.15
+++ class.CoWikiReverseParser.php	29 May 2005 15:39:35 -0000	1.16
@@ -2,7 +2,7 @@
 
 /**
  *
- * $Id: class.CoWikiReverseParser.php,v 1.15 2005/01/16 23:25:09 dgorski Exp $
+ * $Id: class.CoWikiReverseParser.php,v 1.16 2005/05/29 15:39:35 cmarble 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
@@ -17,7 +17,7 @@
  * @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.15 $
+ * @version     $Revision: 1.16 $
  *
  */
 
@@ -30,6 +30,8 @@
  *
  * @author      Daniel T. Gorski, <[email protected]>
  * @since       coWiki 0.3.0
+ * @author      Archie Campbell <[email protected]>
+ * @since       coWiki 0.4.0
  *
  * @todo        [D11N]  Complete documentation
  */
@@ -124,6 +126,8 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
      * @todo    [D11N]  Check description
      */
@@ -140,7 +144,15 @@
 
         // If no referenced document was found
         if (!is_object($RefNode)) {
-            return '['.__('I18N_DOC_DELETED_DOCUMENT').']';
+	    if ($aMatches[0]=='square') {
+                return '['.'['.$aMatches[1].']['.$aMatches[2].']'.
+			__('I18N_DOC_DELETED_DOCUMENT').']';
+
+	    } else {
+		return '['.'('.$aMatches[1].')('.$aMatches[2].')'.
+			__('I18N_DOC_DELETED_DOCUMENT').']';
+
+	    }
         }
 
         // Normalize!
@@ -165,26 +177,53 @@
 
                 // Link directly to a web with an alias
                 if ($aMatches[2] != '') {
+		    if( $aMatches[0]=='square' ) {
+                        return '[['.$sPrefix.']['.escape($aMatches[2]).']]';
+		    } else {
                    return '(('.$sPrefix.')('.escape($aMatches[2]).'))';
                 }
+                }
 
                 // Link directly to a web without alias
+		if ($aMatches[0]=='square') {
+		    return '[['.$sPrefix.']]';
+		} else {
                 return '(('.$sPrefix.'))';
             }
         }
+        }
 
         // ---
 
         // Generate normal link without alias
         if ($aMatches[2] == '') {
+	    if ($aMatches[0]=='square') {
+		return
+		'[['
+                  .$sPrefix
+                  .escape($RefNode->get('name'))
+		.']]';
+	    } else {
             return
               '(('
                   .$sPrefix
                   .escape($RefNode->get('name'))
               .'))';
         }
+        }
 
         // Generate normal link with an alias
+	if ($aMatches[0] == 'square' ) {
+	    return '['
+                 .'['
+                    .$sPrefix
+                    .$this->noopDelimiters(escape($RefNode->get('name')))
+                 .']'
+                 .'['
+                    .escape($aMatches[2])
+                 . ']'
+               .']';
+	} else {
         return '('
                  .'('
                     .$sPrefix
@@ -194,7 +233,7 @@
                     .escape($aMatches[2])
                  . ')'
                .')';
-
+	}
     }
 
     /**
@@ -206,14 +245,24 @@
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
      * @todo    [D11N]  Check description
      */
     protected function buildHyperRefLink(&$aMatches) {
         if (trim($aMatches[2]) == '') {
+	    if( $aMatches[0] == 'square' ) {
+		return '[[' . $aMatches[1] . ']]';
+	    } else {
             return '((' . $aMatches[1] . '))';
         }
+        }
+	if( $aMatches[0] == 'square' ) {
+	    return '[[' . $aMatches[1] . '][' . $aMatches[2] . ']]';
+	} else {
         return '((' . $aMatches[1] . ')(' . $aMatches[2] . '))';
+	}
     }
 
 } // of class

File [added]: class.TestWikiParser.php
Url: http://cowiki.tigris.org/source/browse/cowiki/includes/cowiki/class/parse/class.TestWikiParser.php?rev=1.1&content-type=text/vnd.viewcvs-markup
Added lines: 0
--------------

File [changed]: class.WikiParser.php
Url: http://cowiki.tigris.org/source/browse/cowiki/includes/cowiki/class/parse/class.WikiParser.php?r1=1.49&r2=1.50
Delta lines:  +770 -1855
------------------------
--- class.WikiParser.php	9 Apr 2005 23:34:07 -0000	1.49
+++ class.WikiParser.php	29 May 2005 15:39:35 -0000	1.50
@@ -2,7 +2,7 @@
 
 /**
  *
- * $Id: class.WikiParser.php,v 1.49 2005/04/09 23:34:07 dgorski Exp $
+ * $Id: class.WikiParser.php,v 1.50 2005/05/29 15:39:35 cmarble 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
@@ -21,38 +21,10 @@
  * @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.49 $
+ * @version     $Revision: 1.50 $
  *
  */
 
-    // We should solve this with lex/yacc (flex/bison), but mass hosters
-    // usually do not allow their users to execute binaries. So let's
-    // fight this (dumb) way.
-
-    // On the other hand, the coWiki syntax is quite loose and is not easy
-    // to express by a BNF parser, IMO.
-
-    define('WIKI_TOKEN_NOOP',          1);
-    define('WIKI_TOKEN_PRE',           2);
-    define('WIKI_TOKEN_CODE',          3);
-    define('WIKI_TOKEN_POSTING',       4);
-    define('WIKI_TOKEN_LINK',          5);
-    define('WIKI_TOKEN_URI',           6);
-    define('WIKI_TOKEN_TOC',           7);
-    define('WIKI_TOKEN_PARAM_PLUGIN',  8);
-    define('WIKI_TOKEN_SIMPLE_PLUGIN', 9);
-    define('WIKI_TOKEN_HEADING',       10);
-    define('WIKI_TOKEN_HR',            11);
-    define('WIKI_TOKEN_LIST',          12);
-    define('WIKI_TOKEN_QUOTE',         13);
-    define('WIKI_TOKEN_VAR',           14);
-    define('WIKI_TOKEN_BREAK',         15);
-    define('WIKI_TOKEN_PARAM_TABLE',   16);
-    define('WIKI_TOKEN_SIMPLE_TABLE',  17);
-    define('WIKI_TOKEN_REM',           18);
-    define('WIKI_TOKEN_SUB',           19);
-    define('WIKI_TOKEN_SUP',           20);
-
 /**
  * coWiki - Wiki parser class
  *
@@ -62,1958 +34,901 @@
  *
  * @author      Daniel T. Gorski, <[email protected]>
  * @since       coWiki 0.3.0
+ * @author      Archie Campbell <[email protected]>
+ * @since       coWiki 0.4.0
  */
 class WikiParser extends Object {
 
     protected static
         $Instance = null;
-
-    // Containers for the <toc> - table of contents (headings).
-    protected
-        $aToc = array(),
-        $sDocToc = false;
-
-    // Container for tokenized strings
-    protected
-        $aToken = array();
-
-    // Elements that do NOT reside in <P>-paragraphs
-    protected
-        $aNonPara = array(
-            WIKI_TOKEN_PRE,
-            WIKI_TOKEN_CODE,
-            WIKI_TOKEN_POSTING,
-            WIKI_TOKEN_TOC,
-            WIKI_TOKEN_PARAM_PLUGIN,
-            WIKI_TOKEN_SIMPLE_PLUGIN,
-            WIKI_TOKEN_HEADING,
-            WIKI_TOKEN_HR,
-            WIKI_TOKEN_LIST,
-            WIKI_TOKEN_QUOTE,
-            WIKI_TOKEN_PARAM_TABLE,
-            WIKI_TOKEN_SIMPLE_TABLE,
-            WIKI_TOKEN_SUB,
-            WIKI_TOKEN_SUP
-        );
-
-    // Helper
+    protected $aToc = array(),
+	      $aRows = array(),
+              $iRow = 0;
     protected
-        $RefNodes    = null,
         $aList       = array(),
-        $aTblRowNoop = array();
-
+        $aTable = array(),
+        $t = 0,
+        $l = 0,
+	$bList = false,
+	$bTable = false;
+    protected $RefNodes = null;
     // --------------------------------------------------------------------
 
     /**
-     * Get instance
+     * Collect referenced nodes to where the document is linking to
      *
-     * @access  public
+     * @access  protected
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    public function getInstance() {
-        if (!self::$Instance) {
-            self::$Instance = new WikiParser;
-        }
-        return self::$Instance;
+    protected function addReferencedNode($Obj) {
+        $this->RefNodes->add($Obj);
     }
 
     // --------------------------------------------------------------------
 
     /**
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function __construct() {}
-
-    // --- Helper methods -------------------------------------------------
-
-    /**
-     * Set doc toc
+     * Get referenced nodes
      *
-     * @access  protected
+     * @access  public
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    protected function setDocToc($sStr) {
-        $this->sDocToc = $sStr;
+    public function getReferencedNodes() {
+        return $this->RefNodes;
     }
 
-    // --------------------------------------------------------------------
-
     /**
-     * Get doc toc
+     * Get instance
      *
-     * @access  protected
+     * @access  public
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    protected function getDocToc() {
-        return $this->sDocToc;
+    public function getInstance() {
+        if (!self::$Instance) {
+            self::$Instance = new WikiParser;
+        }
+        return self::$Instance;
     }
 
     // --------------------------------------------------------------------
 
     /**
-     * Get token count
-     *
      * @access  protected
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    protected function getTokenCount() {
-        return sizeof($this->aToken);
+    protected function __construct() {
     }
 
-    // --------------------------------------------------------------------
+    // --- Helper methods -------------------------------------------------
 
     /**
-     * Set restore flag
-     *
      * @access  protected
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    protected function setRestoreFlag($bFlag) {
-        $this->bRestoreFlag = $bFlag;
-    }
-
-    // --------------------------------------------------------------------
+    protected function restoreTokens($sStr){ return $sStr; }
 
     /**
-     * Get restore flag
+     * Generate "toc" (Document Table of Contents)
      *
      * @access  protected
      *
      * @author  Daniel T. Gorski, <[email protected]>
      * @since   coWiki 0.3.0
      */
-    protected function getRestoreFlag() {
-        return $this->bRestoreFlag;
-    }
+    protected function buildToc(&$i, &$nDepth) {
+        $sStr = '';
 
-    // --------------------------------------------------------------------
+        // Iterate though all toc-entries
+        while (isset($this->aToc[$i]['DEPTH'])
+               && $this->aToc[$i]['DEPTH'] == $nDepth) {
 
-    /**
-     * Tokenize
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenize($sStr, $nType, $mMeta = false) {
-        $i = $this->getTokenCount();
+	    $sAlias = $this->aToc[$i]['TEXT'];
+            $sStr .= '<li>';
+            $sStr .=    '<link topicref="'.($i+1).'">'.$sAlias.'</link>';
+
+            $i++;
+
+            // Recurse (indent) if toc-entry is nested
+            if (isset($this->aToc[$i]['DEPTH'])) {
 
-        $this->aToken[$i]['CONTENT'] = $sStr;
-        $this->aToken[$i]['TYPE']    = $nType;
-        $this->aToken[$i]['META']    = $mMeta;
+                if ($this->aToc[$i]['DEPTH'] > $nDepth) {
+                    $sStr .= '<ul>';
+                    $sStr .=   $this->buildToc($i, $this->aToc[$i]['DEPTH']);
+                    $sStr .= '</ul>';
+                }
+            }
 
-        // (Token-)Elements which do NOT reside in <P>-paragraphs, are
-        // marked with a leading \r as such.
-        if (in_array($nType, $this->aNonPara)) {
-            return "\r\t" . $i . "\t";
+            $sStr .= '</li>';
         }
 
-        return "\t" . $i . "\t";
+        return $sStr;
     }
 
     // --------------------------------------------------------------------
 
+    function finalizeList( &$sRow )
+    {
+      $debug = false;
+	//end of list
+	if($this->bList) {
+	    $d = 0;
+	    $sListType = $this->aList[$d]['TYPE'];
+	    $nDepth = $this->aList[$d]['DEPTH'];
+	    $sContent = '<p><list>'.
+			    '<'.$sListType.'>'.
+			      $this->buildList($d,$nDepth,$this->aList).
+			    '</'.$sListType.'>'.
+	             '</list></p>';
+    	    $this->bList = false;
+	    $this->aList = array();
+	    $this->l = -1;
+	    if($debug){ echo "finalizedList ".($this->bTable?"table":"ret")."\n"; }
+	    if($this->bTable){
+	        array_push( $this->aTable[$this->t], $sContent );
+	    }else{
+		return $sContent;
+	    }
+	//more table
+	}else if($this->bTable) {
+	    if ($debug) { echo "fin table row\n"; }
+	    array_push( $this->aTable[$this->t], $sRow );
+	    $sRow = '';
+	//non table/list row
+	}else{
+	    if ($debug) { echo "fin non table/list row\n"; }
+	    $sContent = $this->processContent( $sRow );
+	    $sRow = '';
+	    if( $sContent != '' ){
+	      return '<p>'.$sContent.'</p>';
+	    }
+	}
+    }
+
     /**
-     * Restore tokens
+     * parse
      *
-     * @access  protected
+     * @access  public
+     * @param   string  The wiki source string
+     * @return  string  Wiki XML
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function restoreTokens($sStr) {
+    public function parse($sStr) {
 
-        while (true) {
-            $this->setRestoreFlag(false);
+	$sRet = '';
+	$sContent = '';
+	$this->bList = false;
+	$this->bTable = false;
+	$bBegin = false;
+	$this->aList = array();
+	$this->aTable = array();
+
+	$debug = false;
+        // {{{ DEBUG }}}
+        Logger::info('Start parsing wiki document.');
+
+        // Init reference collection
+        $this->RefNodes = new Vector;
+
+        // ---
+
+        $sStr = 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);
+	$sStr = str_replace("\r",   "\n", $sStr);
+
+	$this->aRows = explode( "\n",$sStr."\n" );
 
-            $sStr = preg_replace_callback(
-                '=\r?\t([0-9]+)\t=US',
-                array(&$this, 'invokeTokens'),
-                $sStr
-            );
 
-            if ($this->getRestoreFlag() == false) {
+        $this->iRow = 0;
+	$sRow = $this->aRows[$this->iRow];
+	$bBegin = true;
+
+	$this->t = 0; $this->l = -1;
+
+	while( true ) {
+	    $sContent = '';
+	    $aMatches = array();
+	    //table of contents alone ^<toc/>$
+	    if( $bBegin && preg_match( '=^&lt;toc(/?)&gt;$=',
+					$sRow, $aMatches )) {
+		$sRet .= "\n\t\t\n";
+	    //headings
+	    }else if( $bBegin && preg_match( '=^(\+{1,})\s*(.*)$=s',
+				    $sRow, $aMatches )) {
+	        if ($debug) { echo "headings ".$aMatches[2]."\n"; }
+                $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 .'>';
+	    //end of table
+	    } else if( $bBegin && ($this->t > 0) && 
+			    preg_match( '=^&lt;/table&gt;=i',
+						$sRow, $aMatches  )) {
+	        if($debug) { echo "end of table ".$sRow."\n"; }
+		if($this->bTable && $this->bList)
+		{
+		    $this->finalizeList( $sRow );
+		}
+	        $sParam = trim($this->aTable[$this->t][0]);
+		if( $sParam == '') {
+		    $sRet .= '<table>'.$this->buildTable($this->aTable[$this->t]).
+		             '</table>';
+		} else {
+		    $sRet .= '<table '.trim(unescape($sParam)).'>'.
+			         $this->buildTable($this->aTable[$this->t]).'</table>';
+		}
+		if( --$this->t == 0 ){
+  		    $this->bTable = false;
+		}
+	    //beginning of table
+	    } else if( $bBegin &&
+			preg_match( '=^&lt;table([^>]*)&gt;$=Ui',
+					$sRow, $aMatches )) {
+	        if ($debug) { echo "begin table ".$aMatches[0]."\n"; }
+		$this->bTable = true;
+		$this->aTable[++$this->t] = array();
+		array_push( $this->aTable[$this->t], $aMatches[1] );
+	    //beginning of list
+	    } else if( $bBegin && 
+			preg_match( '=^([\s*#]{0,}[*#]) ([^\n]*)$=',
+				    $sRow, $aMatches )) {
+	        if ($debug) { echo "begin list ".$aMatches[0]."\n"; }
+	        $this->bList = true;
+		$this->aList[++$this->l] = array();
+		$this->aList[$this->l]['TEXT'] = $this->processContent( $aMatches[2] );
+		$this->aList[$this->l]['DEPTH'] = strlen( $aMatches[1] );
+		$this->aList[$this->l]['TYPE'] = (substr($aMatches[1],-1)=='*'?
+							      'ul':'ol');
+	    //horizontal rule
+	    } else if ($bBegin && 
+			preg_match( '=^-{3,}(\s|$)=',
+				    $sRow, $aMatches ) ) {
+	        $sRet .= '<hr/>'."\n";
+	    } else {
+		if($debug) { echo "pre finalize sRow ".$sRow."\n"; }
+		$sRet .= $this->finalizeList( $sRow );
+		if($debug) { echo "post finalize sRow ".$sRow."\n"; }
+	    }
+	    if(isset($aMatches[0])) {
+		$sRow = substr( $sRow, strlen( $aMatches[0] ) );
+		$bBegin = false;
+	    }
+	    if ( trim($sRow) == '' ) {
+		if( ++$this->iRow < sizeof($this->aRows) ) {
+		    if($debug) { echo "newline\n"; }
+		    $sRow = $this->aRows[$this->iRow];
+		    $bBegin = true;
+		} else {
                 break;
             }
         }
+	}
 
-        return $sStr;
+	if( preg_match( '=^(.*)\n\t\t\n(.*)$=s', $sRet, $aMatches )) {
+   	    $this->t = 0;
+	    if (sizeof($this->aToc) > 0) {
+		$sToc = $this->buildToc($this->t, $this->aToc[$this->t]['DEPTH']);
+		if ($sToc != '') {
+		    $sToc = '<toc><ul>' . $sToc . '</ul></toc>';
+		} else {
+		    $sToc = '<toc/>';
+		}
+	    }
+	    return $aMatches[1] . $sToc . $aMatches[2];
     }
 
-    // --------------------------------------------------------------------
+        // {{{ DEBUG }}}
+        Logger::info('Finished parsing wiki document.');
+
+	return $sRet;
+    }
 
     /**
-     * Invoke tokens
+     * processContent
      *
      * @access  protected
+     * @return  string
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function invokeTokens(&$aMatches) {
-        $this->setRestoreFlag(true);
-
-        $sStr  = $this->aToken[$aMatches[1]]['CONTENT'];
-        $nType = $this->aToken[$aMatches[1]]['TYPE'];
-        $mMeta = $this->aToken[$aMatches[1]]['META'];
-
-        $sStr = $this->restoreTokens($sStr);
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
+     */
+    protected function processContent( $sStr ) {
+        $sRet = '';
+       	$aMatches = array();
+
+	$sStr = ltrim( $sStr );
+	$debug = false;
+	if($debug){echo "processContent ".$sStr."\n";}
+	while( $sStr != '' ) {
+	    //paragraphs
+	    if (preg_match( '=^&lt;(/??)p&gt;=i', $sStr, $aMatches) ) {
+	      if($debug){echo "0\n";}
+	    //pre code posting q rem and noop start are greedy until 
+	    // closing tag or eoln anywhere on a line
+	    } else if (preg_match( '=^&lt;(pre|code|posting|q|rem|noop)&gt;=i',
+				    $sStr, $aMatch ) ) {
+	      if($debug){echo "1\n";}
+	        $sStr = substr( $sStr, strlen($aMatch[0]) );
+		$curLine = $this->iRow;
+		$curLen = strlen( $sStr );
+		$sContent = '';
+	        while( !preg_match( '=^&lt;/'.$aMatch[1].'&gt;=is', 
+						$sStr, $aMatches ) ) {
+		    if(preg_match( '#^&lt;[^&]*&gt;#is', $sStr, $aMatches )){
+			$sContent .= $aMatches[0];
+			$sStr = substr( $sStr, strlen( $aMatches[0] ) );
+		    } else if(preg_match( '=^([^&]|(\&(?!l)))*=is', $sStr, $aMatches )){
+		        $sContent .= $aMatches[0];
+			$sStr = substr( $sStr, strlen( $aMatches[0] ) );
+		    }
+		    if ( $sStr == '' ) {
+			if ( ++$this->iRow < sizeof($this->aRows) ) {
+			    $sStr = $this->aRows[$this->iRow];
+			    $sContent .= "\n";
+			} else {
+			    $sStr = $this->aRows[$curLine];
+			    $sStr = substr( $sStr, strlen( $sStr )-$curLen );
+			    break;
+			}
+		    }
+		}
+		if ( $this->iRow < sizeof($this->aRows) ) {
+		    switch($aMatch[1])
+		    {
+			case 'rem':
+			case 'noop':
+			    $sRet .= '<'.$aMatch[1].'>'.$sContent .'</'.$aMatch[1].'>';
+			    break;
+			default:
+			    $aContent = explode( "\n",$sContent );
+			    $sContent = '';
+			    for( $i=0; $i < sizeof($aContent); $i++ ){
+			        $sContent .= $this->processContent($aContent[$i]);
+			    }
+			    $sRet .= '<'.$aMatch[1].'>'.$sContent.'</'.$aMatch[1].'>';
+			    break;
+		    }
+		    $sStr = substr( $sStr, strlen( $aMatches[0] ) );
+		} else {
+		    $this->iRow = $curLine;
+		    $sRet .= '&lt;'.$aMatch[1].'&gt;';
+		}
+		$aMatches = array();
+	    //line-break
+	    }else if( preg_match( '=^&lt;br(/?)&gt;=is', $sStr, $aMatches )) {
+	      if($debug){echo "2\n";}
+	        $sRet .= '<br/>';
+	    //variables
+	    }else if( preg_match( '=^%[A-Z0-9_]+$=is', $sStr, $aMatches ) ) {
+	      if($debug){echo "3\n";}
+	        $sRet .= $aMatches[0];
+	    }else if( preg_match( '=^%\s+=s', $sStr, $aMatches ) ) {
+	      if($debug){echo "4\n";}
+	        $sRet .= $aMatches[0];
+	    }else if( preg_match( '=^%([A-Z0-9_]+)%=Ui', $sStr, $aMatches )) {
+	      if($debug){echo "5\n";}
+	        $sRet .= '<var name="'.$aMatches[1].'"/>';
+	    //links (()())
+	    }else if( preg_match( '=^\(\('.
+				    '([^\(\)]+\)\([^\(\)]+)'.
+				    '\)\)=Usx',
+				  $sStr, $aMatches )) {
+	      if($debug){echo "6\n";}
+	        $sRet .= $this->createLinkElement( $aMatches[1] );
+	    //links [[][]]
+	    }else if( preg_match( '=^\[\['.
+				    '([^\[\]]+\]\[[^\[\]]+)'.
+				    '\]\]=Usx',
+				  $sStr, $aMatches )) {
+	      if($debug){echo "7\n";}
+	        $sRet .= $this->createLinkElement( $aMatches[1], true );
+	    }else if( preg_match( '=^\(\([^\(\)]+\)?=Ui', $sStr, $aMatches )) {
+	      if($debug){echo "8\n";}
+	        $sRet .= $aMatches[0];
+	    }else if( preg_match( '=^\(?[^\(\)]+\)\)=Ui', $sStr, $aMatches )) {
+	      if($debug){echo "9\n";}
+ 	        $sRet .= $aMatches[0];
+	    }else if( preg_match( '=^[^\(\)]+\)\([^\(\)]+=Ui', $sStr, $aMatches )) {
+	      if($debug){echo "10\n";}
+	        $sRet .= $aMatches[0];
+		/*
+	    //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>';
+		*/
+		/*
+	    //urls
+	    }else if( preg_match( '=^&lt;url(\s+)(
+                   (http://|https://|ftp://|mailto:|news:)
+                   ([-_A-Z0-9\S]*)
+                   (\*\s|\=\s|&quot;|&lt;|&gt;|<|>|\(|\)|\s|$)??
+				    )&gt;=six',
+		    $sStr, $aMatches ) ) {
+		*/
+	    //URIs
+	    }else if( preg_match(
+		'=^(http://|https://|ftp://|mailto:|news:)
+                   ([-\._A-Z0-9\S]*)
+                   (\*\s|\=\s|&quot;|&lt;|&gt;|<|>|\(|\)|\s|$)??
+                 =six',
+				  $sStr, $aMatches )) {
+	      if($debug){echo "12\n";}
+	        $sRet .= '<uri strref="' . 
+				$aMatches[1].$aMatches[2] . '"/>';
+		/*
+	    }else if( preg_match( '=^&lt;plugin +([A-Za-z0-9_.]+)( +([^&]*))?(/?)&gt;=i',
+				    $sStr, $aMatches )) {
+	    }else if( preg_match( '=^&lt;plugin +([A-Za-z0-9_\.]+)(([^&]|&(?!g))*)(/?)&gt;=i',
+				    $sStr, $aMatches )) {
+		*/
+   	    //plugins
+	    }else if( preg_match( '=^&lt;plugin +([A-Za-z0-9_\.]+)( +([^&]|&(?!g))*)*(/?)&gt;=i',
+				    $sStr, $aMatches )) {
+	      if($debug){echo "13 plugin ".$aMatches[0]."\n";}
+	        if(isset($aMatches[2]) && $aMatches[2] != ''){
+		    $aMatches[2] = str_replace("&quot;",'"',$aMatches[2]);
+		    $sRet .= '<plugin name="'.
+				$aMatches[1].'"'.$aMatches[2].'/>';
+		}else{
+		    $sRet .= '<plugin name="'.$aMatches[1].'"/>';
+		}
+	    //emphasis, subscript, superscript, justification
+	    }else if( preg_match( '#^&lt;(/??)(strike|tt|b|u|i|sub|sup|left|center|right)&gt;#is',
+	 	      $sStr, $aMatches ) ) {
+	      if($debug){echo "14\n";}
+	        switch($aMatches[2]{0}){
+		    case 'l':
+		    case 'L':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x1E";
+			} else {
+			    $sRet .= "\x19";
+			}
+			break;
+		    case 'c':
+		    case 'C':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x1F";
+			} else {
+			    $sRet .= "\x1A";
+			}
+			break;
+		    case 'r':
+		    case 'R':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x7F";
+			} else {
+			    $sRet .= "\x1B";
+			}
+			break;
+		    case 's':
+		    case 'S':
+		        if(strtolower($aMatches[2])=="sub") {
+			    if( $aMatches[1] =='/') {
+			        $sRet .= "\x1C";
+			    } else {
+			        $sRet .= "\x17";
+			    }
+		        } else if(strtolower($aMatches[2])=="sup") {
+			    if( $aMatches[1] =='/') {
+			        $sRet .= "\x1D";
+			    } else {
+			        $sRet .= "\x18";
+			    }
+			} else if(strtolower($aMatches[2])=="strike") {
+			    if( $aMatches[1]=='/' ) {
+				$sRet .= "\x12";
+			    } else {
+				$sRet .= "\x06";
+			    }
+			}
+			break;
+		    case 't':
+		    case 'T':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x13";
+			} else {
+			    $sRet .= "\x0E";
+			}
+			break;
+		    case 'b':
+		    case 'B':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x14";
+			} else {
+			    $sRet .= "\x0F";
+			}
+			break;
+		    case 'u':
+		    case 'U':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x15";
+			} else {
+			    $sRet .= "\x10";
+			}
+			break;
+		    case 'i':
+		    case 'I':
+		        if( $aMatches[1]=='/' ) {
+			    $sRet .= "\x16";
+			} else {
+			    $sRet .= "\x11";
+			}
+		    break;
+	        }
+	    //emphasis						
+	    }else if( preg_match( '#^([-=\*_/]{2})#',
+	 	      $sStr, $aMatches ) ) {
+	      if($debug){echo "15\n";}
+	        if(preg_match( '#^['.$aMatches[1]{0}.']{1,}#',
+			substr($sStr,strlen($aMatches[0])), $aHead) ) {
+		    $aMatches[0] .= $aHead[0];
+		    $sRet .= $aMatches[0];
+		} else {
+		    switch($aMatches[1]{0}){
+			case '-':
+			    $sRet .= "\x01";
+			break;
+			case '=':
+			    $sRet .= "\x02";
+			break;
+			case '*':
+			    $sRet .= "\x03";
+			break;
+			case '_':
+			    $sRet .= "\x04";
+			break;
+			case '/':
+			    $sRet .= "\x05";
+			break;
+		    }
+	        }
+	    //unknown tags
+	    }else if( preg_match( '=^&lt;[^>&]*=U', $sStr, $aMatches ) ) {
+	      if($debug){echo "11 ".$aMatches[0]."\n";}
+	        $sRet .= $aMatches[0];
+	    }else if( preg_match( '#^&lt;[^>&]*&gt;#', $sStr, $aMatches ) ) {
+	      if($debug){echo "16 unknown ".$aMatches[0]."\n";}
+	        $sRet .= $aMatches[0];
+		/*
+            }else if( preg_match( '#^((([-=\*_/]{1})(?!\3))|[^-=\*_&%\(\[]|(&?!l))#Ui',
+		*/
+            }else if( preg_match( '#^((([-=\*_/]{1})(?!\3))|[^-=\*_&%\(\[]|&(?!l))#i',
+				    $sStr, $aMatches ) ) {
+	      if($debug){echo "17 ".$aMatches[0]."\n";}
+	        $sRet .= $aMatches[0];
+	    }
+	    if($debug){echo $sStr."\n";}
+	    if( isset($aMatches[0]) ) {
+	        $sStr = substr( $sStr, strlen($aMatches[0]) );
+	    }
+	}
+	//process emphasis tokens
+	$sStr = $sRet;
+	$sRet = '';
+	$p = 0;
+	//while found wiki tokens (count)
+	  //convert in pairs or replace (as we go)
+	$this->replaceWikiTokens("\x01","-","strike",$sStr);
+	$this->replaceWikiTokens("\x02","=","tt",$sStr);
+	$this->replaceWikiTokens("\x03","*","b",$sStr);
+	$this->replaceWikiTokens("\x04","_","u",$sStr);
+	$this->replaceWikiTokens("\x05","/","i",$sStr);
+	//while found html tokens (paired)
+	  //correspond or replace (suitably as we go)
+	$this->replaceHtmlTokens("\x06","\x12","strike",$sStr);
+	$this->replaceHtmlTokens("\x0E","\x13","tt",$sStr);
+	$this->replaceHtmlTokens("\x0F","\x14","b",$sStr);
+	$this->replaceHtmlTokens("\x10","\x15","u",$sStr);
+	$this->replaceHtmlTokens("\x11","\x16","i",$sStr);
+	$this->replaceHtmlTokens("\x17","\x1C","sub",$sStr);
+	$this->replaceHtmlTokens("\x18","\x1D","sup",$sStr);
+	$this->replaceHtmlTokens("\x19","\x1E","left",$sStr);
+	$this->replaceHtmlTokens("\x1A","\x1F","center",$sStr);
+	$this->replaceHtmlTokens("\x1B","\x7F","right",$sStr);
+
+	if($debug) { echo "processContent returns ".$sStr."\n"; }
+	return $sStr;
+    }
+
+    protected function replaceWikiTokens( $char,$wiki,$sHtml, &$sStr ) {
+        $sRet = '';
+	$open = false;
+ 	$c = substr_count($sStr,$char);
+	if(!$c) { return; }
+        while( $c ) {
+ 	    if($sStr{0} == $char){
+   	        if($c >= 2 || ($c == 1 && $open) ) {
+ 		    $sRet .= '<'.($open?'/':'').$sHtml.
+				    ($open?'':' origin="wiki"').'>';
+		    $open = !$open;
+		    $c--;
+		} else {
+		    $sRet .= str_repeat($wiki,2);
+		    $c--;
+		}
+	    } else {
+	        $sRet .= $sStr{0};
+	    }	    
+	    $sStr = substr($sStr,1);
+	}
+	$sRet .= $sStr;
+	$sStr = $sRet;
+    }
+
+    protected function replaceHtmlTokens( $start, $end, $sHtml, &$sStr ) {
+        $sRet = '';
+	$open = false;
+	$s = substr_count($sStr,$start);
+	$e = substr_count($sStr,$end);
+	if(!$s && !$e) { return; }
+	while( $s || $e ) {
+	    if($sStr{0} == $start){
+	        if($e > 0){
+		    $sRet .= '<'.$sHtml.' origin="html">';
+		    $open = true;
+		} else {
+		    $sRet .= '&lt;'.$sHtml.'&gt;';
+		}
+		$s--;
+	    } else if($sStr{0} == $end){
+	        if($open){
+		    $sRet .= '</'.$sHtml.'>';
+		    $open = false;
+		} else {
+ 		    $sRet .= '&lt;/'.$sHtml.'&gt;';
+		}
+		$e--;
+	    } else {
+	        $sRet .= $sStr{0};
+	    }
+	    $sStr = substr($sStr,1);
+	}
+	$sRet .= $sStr;
+	$sStr = $sRet;	
+    }
 
-        switch ($nType) {
-            case WIKI_TOKEN_NOOP:
-                $sStr = $this->createNoopElement($sStr);
-                break;
+    /**
+     * &build table
+     *
+     * @access  protected
+     * @return  string
+     *
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
+     */
+    protected function buildTable( &$aTable ) {
 
-            case WIKI_TOKEN_REM:
-                $sStr = $this->createRemarkElement($sStr);
-                break;
+	$sRet = '';
+	$sContent = '';
+	$sRow = '';
+	$bPriorRow = false;
+	$aMatches = array();
 
-            case WIKI_TOKEN_PRE:
-                $sStr = $this->createPreElement($sStr);
-                break;
+	$debug = false;
 
-            case WIKI_TOKEN_CODE:
-                $sStr = $this->createCodeElement($sStr);
-                break;
+	$j = 0;
+	$v = sizeof($aTable);
 
-            case WIKI_TOKEN_POSTING:
-                $sStr = $this->createPostingElement($sStr);
-                break;
+	if($debug) { echo "aTable>".implode("\n",$this->aTable)."\n"; }
 
-            case WIKI_TOKEN_LINK:
-                $sStr = $this->createLinkElement($sStr);
-                break;
+	while( ++$j < $v ) {
 
-            case WIKI_TOKEN_URI:
-                $sStr = $this->createUriElement($sStr);
-                break;
+	  /*
+            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];
 
-            case WIKI_TOKEN_TOC:
-                $sStr = $this->createTocElement();
-                $this->setDocToc($sStr);
-                break;
+ 	    if ($debug) { echo "while j ".$j." v ".$v." srow ".$sRow."\n"; }
 
-            case WIKI_TOKEN_PARAM_PLUGIN:
-                $sStr = $this->createParamPluginElement($sStr, $mMeta);
-                break;
+	    //find table syntax within a (long?) row
+	    while( strlen($sRow) && preg_match(
+		    '=^([\|!]+[-+]?)( {0,1}[^\|!\n]*)?=',
+		    $sRow,
+		    $aMatches) ) {
 
-            case WIKI_TOKEN_SIMPLE_PLUGIN:
-                $sStr = $this->createSimplePluginElement($sStr);
-                break;
+		//found exploded row begins with pipe syntax, poss params
+		$sRow = substr( $sRow, strlen($aMatches[0]) );
 
-            case WIKI_TOKEN_HEADING:
-                $sStr = $this->createHeadingElement($sStr, $mMeta);
-                break;
+		$nType = 0;
+		//tables made of cells, headers, rows, caption
+		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;
+		}else if ($aMatches[1]{0}=="|") {
+		    $sRet .= '<td';
+		    if ($aMatches[1] == "||") {
+		        $nType = 4;
+			$sRet .= ' origin="doubled"';
+		    } else {
+			$nType = 1;
+		    }
+		}else if ($aMatches[1]{0}=="!") {
+		    $sRet .= '<th';
+		    $nType = 2;
+		}
+		//content can be large, room for it here
+		$sContent = '';
 
-            case WIKI_TOKEN_LIST:
-                $sStr = $this->createList($sStr);
-                break;
+		//left a space to denote no params
+		if ($aMatches[2]{0} == ' ' || $aMatches[1] == "||") {
+		    $sRet .= '>';
+		    $sContent .= substr($aMatches[2],1).$sRow;
+		//expect double pipe, bang or newline
+		} elseif (trim($aMatches[2])!='') {
+		    //got params. look for pipe syntax that blocked last preg
+		    $sRet .= ' '.unescape($aMatches[2]).'>';
 
-            case WIKI_TOKEN_SIMPLE_TABLE:
-                $sStr = $this->createSimpleTableElement($sStr);
-                break;
+		    //got rest of pipe syntax (...|text)
+		    if (preg_match('=^\|(.*)$=',$sRow,$aMatches)) {
+			$sContent .= $aMatches[1];
+			$sRow = substr( $sRow, strlen($aMatches[0]) );
+		    }
+		} else {
+		    $sRet .= '>';
+		}
 
-            case WIKI_TOKEN_PARAM_TABLE:
-                $sStr = $this->createParamTableElement($sStr, $mMeta);
-                break;
+		if($debug) { echo "sret ".$sRet." sRow ".$sRow." cont ".$sContent."\n"; }
 
-            case WIKI_TOKEN_HR:
-                $sStr = $this->createRuleElement();
+		//look ahead now.
+		if (trim($sRow) == '' && $nType != 4) {
+		    //look ahead to close our content
+		    while( ++$j<$v ){
+			if($debug) { echo " j ".$j." v ".$v."\n"; }
+			$sRow = $aTable[$j];
+			//pipe syntax (will force $sContent) to continue
+			if ($sRow{0} == '|' || $sRow{0} == '!') {
+			    if($debug) { echo "pipe ".$sRow."\n"; }
+			    $sRet .= $this->processContent($sContent);
+			    switch($nType){
+			      case 1:
+			        $sRet .= "</td>\n";
                 break;
-
-            case WIKI_TOKEN_QUOTE:
-                $sStr = $this->createQuoteElement($sStr);
+			      case 2:
+			        $sRet .= "</th>\n";
                 break;
+			    }
+			    --$j;
+			    continue 3;
+			} else {
+			    //non-pipe (ever-increasing) content
+			    if($debug) { echo "nonpipe ".$sContent."\n"; }
+			    $sContent .= $sRow;
+			}
+		    }
 
-            case WIKI_TOKEN_VAR:
-                $sStr = $this->createVarElement($sStr, $mMeta);
+		    $j = $v;
+		    $sRet .= $this->processContent($sContent);
+		    switch($nType){
+		      case 1:
+		        $sRet .= "</td>\n";
                 break;
-
-            case WIKI_TOKEN_BREAK:
-                $sStr = $this->createBreakElement();
+		      case 2:
+		        $sRet .= "</th>\n";
                 break;
-
-            case WIKI_TOKEN_SUB:
-                $sStr = $this->createSubElement($sStr);
+		    }
+		    continue 2;
+		} elseif ( ( $sRow{0} == "|" || $sRow{0} == "!") && 
+			($sRow{1} == "|" || $sRow{1} == "!") ||
+			    ( $nType == 4 ) ) {
+		    $sRow = substr($sRow, 1);
+		    $sRet .= $this->processContent($sContent);
+		    switch($nType){
+		      case 1:
+		      case 4:
+			$sRet .= "</td>\n";
                 break;
-
-            case WIKI_TOKEN_SUP:
-                $sStr = $this->createSupElement($sStr);
+		      case 2:
+			$sRet .= "</th>\n";
                 break;
         }
-
-        return $sStr;
+		    continue 1;
+	        }
+	    }
+	}
+	if( $bPriorRow ) {
+	  if($debug) { echo $sRet."</tr>\n"; }
+	    return $sRet."</tr>";
+	} else {
+	  if($debug) { echo $sRet."\n"; }
+	    return $sRet;
     }
-
-    // --- 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
+     * &build list
      *
      * @access  protected
+     * @return  string
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      */
-    protected function createRemarkElement(&$sStr) {
-        return '<rem>'.$sStr.'</rem>';
-    }
+    protected function buildList(&$i, &$nDepth, &$aList) {
+        $sStr = '';
 
-    // --------------------------------------------------------------------
+        // Iterate though all list-entries
+        while (isset($aList[$i]['DEPTH'])
+               && $aList[$i]['DEPTH'] == $nDepth) {
 
-    /**
-     * 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>";
-    }
+            $sStr .= '<li>';
+            $sStr .=    $aList[$i]['TEXT'];
 
-    // --------------------------------------------------------------------
+            $i++;
 
-    /**
-     * 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>";
-    }
+            // Recurse (indent) if list-entry is nested
+            if (isset($aList[$i]['DEPTH'])) {
 
-    // --------------------------------------------------------------------
+                if ($aList[$i]['DEPTH'] > $nDepth) {
 
-    /**
-     * 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>";
-    }
+                    // Correct items that are indented too deep
+                    $aList[$i]['DEPTH'] = $nDepth + 1;
 
-    // --------------------------------------------------------------------
+                    $sListType = $aList[$i]['TYPE'];
 
-    /**
-     * 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.'"/>';
+                    $sStr .=  '<'.$sListType.'>';
+                    $sStr .=    $this->buildList(
+                                    $i,
+                                    $aList[$i]['DEPTH'],
+				    $aList
+                                );
+                    $sStr .=  '</'.$sListType.'>';
+                }
     }
 
-    // --------------------------------------------------------------------
-
-    /**
-     * 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);
+            $sStr .= '</li>';
         }
-        return '<plugin name="'.$sStr.'" '.trim(unescape($sAttr)).'/>';
+
+        return $sStr;
     }
 
     // --------------------------------------------------------------------
-
-    /**
-     * 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>';
-    }
-
-    // --- 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) {
-
-            $sAlias = preg_replace(
-                        '=<link[^>]*>=US',
-                        '',
-                        $this->aToc[$i]['TEXT']
-                      );
-
-            // replace link tokens
-            preg_match_all(
-                '=\r?\t([0-9]+)\t=US',
-                $sAlias,
-                $aMatchesAll,
-                PREG_SET_ORDER
-            );
-
-            foreach($aMatchesAll as $aMatches) {
-                if ($this->aToken[$aMatches[1]]['TYPE'] == WIKI_TOKEN_LINK) {
-                    $sLinkElement = $this->createLinkElement(
-                        $this->aToken[$aMatches[1]]['CONTENT']
-                    );
-
-                    preg_match(
-                        '=<link\s+href\="([^>]+)">([^<]*)</link>=US',
-                        $sLinkElement,
-                        $aLinkMatches
-                    );
-
-                    $sAlias = !empty($aLinkMatches[2])
-                            ? str_replace(
-                                $aMatches[0],
-                                $aLinkMatches[2],
-                                $sAlias
-                            )
-                            : str_replace(
-                                $aMatches[0],
-                                $aLinkMatches[1],
-                                $sAlias
-                            );
-                }
-            }
-
-            $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) {
-
-        // {{{ DEBUG }}}
-        Logger::info('Start parsing wiki document.');
-
-        // ---
-
-        $Registry = Registry::getInstance();
-
-        // Init reference collection
-        $this->RefNodes = new Vector;
-
-        $sStr = trim(escape($sStr));
-
-        // WARNING: Be VERY VERY careful! The order of these processing
-        // steps is EXTREMELY important. Change only if you know what
-        // you are doing! AND then: test it, test it and test it again.
-
-        // 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);
-
-        // Let the string always start and end with a newline (\n).
-        // This makes pattern matching much easier.
-        $sStr = "\n" . $sStr . "\n";
-
-        // --- Tokenizing and preparing -----------------------------------
-
-        // After this step all <noop></noop> are tokenized for further
-        // processing.
-        $sStr = $this->processNoOperation($sStr);
-
-        // After this step all <rem></rem> are tokenized for further
-        // processing.
-        $sStr = $this->processRemark($sStr);
-
-        // After this step all <pre></pre> are tokenized for further
-        // processing.
-        $sStr = $this->processPre($sStr);
-
-        // After this step all <code></code> are tokenized for further
-        // processing.
-        $sStr = $this->processCode($sStr);
-
-        // After this step all <posting></posting> are tokenized for further
-        // processing.
-        $sStr = $this->processPosting($sStr);
-
-        // After this step all parameterized <plugin ... param> are
-        // tokenized for further processing.
-        $sStr = $this->processParamPlugin($sStr);
-
-        // After this step all simple <plugin ...> are tokenized for further
-        // processing.
-        $sStr = $this->processSimplePlugin($sStr);
-
-        // After this step all bracket-links ((foo)), ((foo)(bar)) are
-        // tokenized for further processing.
-        $sStr = $this->processLink($sStr);
-
-        // After this step all clickable URIs (http:// etc.) are tokenized
-        // for further processing.
-        $sStr = $this->processClickable($sStr);
-
-        // After this step all WikiWords are tokenized for further processing.
-        if ($Registry->get('RUNTIME_WIKIWORDS')) {
-            $sStr = $this->processWikiWord($sStr);
-        }
-
-        // After this step all variables are tokenized for further
-        // processing.
-        $sStr = $this->processVariable($sStr);
-
-        // After this step all <toc ...> are tokenized for further
-        // processing.
-        $sStr = $this->processToc($sStr);
-
-        // After this step all emphasis markups (bold, italic, ...) are
-        // converted. These markups are not tokenized.
-        $sStr = $this->processEmphasis($sStr);
-
-        // After this step all headings (+, ++, +++ -> H1, H2, H3 etc.) are
-        // tokenized for further processing.
-        $sStr = $this->processHeading($sStr);
-
-        // After this step all horizontal rules (---) are tokenized for
-        // further processing.
-        $sStr = $this->processHorizontalRule($sStr);
-
-        // After this step all breaks (<br>) are tokenized for further
-        // processing.
-        $sStr = $this->processBreak($sStr);
-
-        // After this step all subs (<sub>) are tokenized for further
-        // processing.
-        $sStr = $this->processSub($sStr);
-
-        // After this step all sups (<sup>) are tokenized for further
-        // processing.
-        $sStr = $this->processSup($sStr);
-
-        // After this step lists are tokenized for further processing.
-        $sStr = $this->processList($sStr);
-
-        // After this step all parameterized <table></table> are tokenized
-        // for further processing.
-        $sStr = $this->processParamTable($sStr);
-
-        // After this step all simple <table></table> are tokenized for
-        // further processing.
-        $sStr = $this->processSimpleTable($sStr);
-
-        // After this step all (block)-quotes are tokenized for further
-        // processing.
-        $sStr = $this->processQuote($sStr);
-
-        // ----------------------------------------------------------------
-
-        // Insert paragraphs
-        $sStr = $this->invokeParagraphs($sStr);
-
-        // Restore references (tokens)
-        $sStr = $this->restoreTokens($sStr);
-
-        // <pre> or <code> escaped the paragraph invocation by replacing
-        // their newline delimiters to \r.
-        // Now we replace the delimiters back into \n again.
-        $sStr = str_replace("\r", "\n", $sStr);
-
-        // {{{ DEBUG }}}
-        Logger::info('Finished parsing wiki document.');
-
-        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(
-            '=&lt;noop&gt;(.+)&lt;/noop&gt;=Usi',
-            array(&$this, 'tokenizeNoOperation'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize no operation
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeNoOperation(&$aMatches) {
-       return $this->tokenize($aMatches[1], WIKI_TOKEN_NOOP);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=&lt;rem&gt;(.+)&lt;/rem&gt;=Usi',
-            array(&$this, 'tokenizeRemark'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize remark
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeRemark(&$aMatches) {
-       return $this->tokenize($aMatches[1], WIKI_TOKEN_REM);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;pre&gt;\n(.+)\n&lt;/pre&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizePre'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize pre
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizePre(&$aMatches) {
-        return $this->tokenize(
-                  $aMatches[1], WIKI_TOKEN_PRE
-               )
-               .$aMatches[2];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;code&gt;\n(.+)\n&lt;/code&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizeCode'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize code
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeCode(&$aMatches) {
-       return $this->tokenize(
-                  $aMatches[1],
-                  WIKI_TOKEN_CODE
-              )
-              .$aMatches[2];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;posting&gt;\n(.+)\n&lt;/posting&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizePosting'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize posting
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizePosting(&$aMatches) {
-       return $this->tokenize(
-                  $aMatches[1], WIKI_TOKEN_POSTING
-              )
-              .$aMatches[2];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * ((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;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize link
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeLink(&$aMatches) {
-        return $this->tokenize($aMatches[1], WIKI_TOKEN_LINK);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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\[\{\']|&quot;)([A-Z][a-z]+([A-Z][a-z]+)+)=',
-            array(&$this, 'tokenizeWikiWord'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize wiki word
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeWikiWord(&$aMatches) {
-        $sLink = preg_replace('=([A-Z])=', ' \1', $aMatches[2]);
-        return $aMatches[1].$this->tokenize(ltrim($sLink), WIKI_TOKEN_LINK);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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|&quot;|&lt;|&gt;|<|>|\(|\)|\s|$)
-             =Usmix',
-            array(&$this, 'tokenizeClickable'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize clickable
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeClickable(&$aMatches) {
-       return $this->tokenize(
-                  $aMatches[1] . $aMatches[2], WIKI_TOKEN_URI
-              )
-              .$aMatches[3];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * <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&lt;toc&gt;=Usi',
-            array(&$this, 'tokenizeToc'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize toc
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeToc(&$aMatches) {
-        return $this->tokenize(false, WIKI_TOKEN_TOC);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * <plugin ...>
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function processSimplePlugin(&$sStr) {
-
-        // Treat <plugin ...>
-        $sStr = preg_replace_callback(
-            '=&lt;plugin +([a-z0-9_.]+)&gt;=Usi',
-            array(&$this, 'tokenizeSimplePlugin'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize simple plugin
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeSimplePlugin(&$aMatches) {
-        return $this->tokenize($aMatches[1], WIKI_TOKEN_SIMPLE_PLUGIN);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * <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(
-            '=&lt;plugin +([a-z0-9_.]+) +(.*)&gt;=Usi',
-            array(&$this, 'tokenizeParamPlugin'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize param plugin
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeParamPlugin(&$aMatches) {
-        return $this->tokenize(
-                  $aMatches[1], WIKI_TOKEN_PARAM_PLUGIN, $aMatches[2]
-               );
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Emphasis: bold, italic, monospaced ...
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function processEmphasis(&$sStr) {
-
-        // These are the possible start delimiters for bold, italic
-        // and monospace emphasis markups
-        $sStart = '(\[\'';
-
-        // These are the possible end delimiters for bold, italic
-        // and monospace emphasis markups
-        $sEnd = '\s,.;:!?()\[\]_-';
-
-        // Italic first, because we do not want to match "</foo>/bar"
-        $sStr = preg_replace(
-            '~  ([^<])?
-                (\s|=|\*|&lt;br&gt;|&quot;|['.$sStart.'])
-                (/)
-                ([^ \n/](\S?|.*(\S|\t)))
-                \3
-                (?=['.$sEnd.']|\*|&lt;br&gt;|&quot;|=)      # do not eat up
-                                                            # trailing chars
-            ~Umix',
-            '\1\2<em>\4</em>\7',
-            $sStr
-        );
-
-        // Bold
-        $sStr = preg_replace(
-            '~  (\s|=|<em>|&lt;br&gt;|&quot;|['.$sStart.'])
-                (\*)
-                ([^ \n\*](\S?|.*(\S|\t)))
-                \2
-                (?=['.$sEnd.']|=|&lt;br&gt;|&quot;|</em>)   # do not eat up
-                                                            # trailing chars
-            ~Umix',
-            '\1<strong>\3</strong>\6',
-            $sStr
-        );
-
-        // Mono
-        $sStr = preg_replace(
-            '~  (\s|<em>|<strong>|&quot;|&lt;br&gt;|['.$sStart.'])
-                (=)
-                ([^ \n=](\S?|.*(\S|\t)))
-                \2
-                (?=['.$sEnd.']|&lt;br&gt;|&quot;|</em>|</strong>)  # do not
-                                                                   # eat up
-                                                                   # trailing
-                                                                   # chars
-            ~Umix',
-            '\1<tt>\3</tt>\6',
-            $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;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize variable
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeVariable(&$aMatches) {
-        return $this->tokenize(false, WIKI_TOKEN_VAR, $aMatches[1]);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize heading
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeHeading(&$aMatches) {
-
-        $nDepth = strlen($aMatches[1]);
-
-        // Remember headings for <toc> - table of contents
-        $i = sizeof($this->aToc);
-        $this->aToc[$i]['TEXT']  = $aMatches[2];
-        $this->aToc[$i]['DEPTH'] = $nDepth;
-
-        return $this->tokenize($aMatches[2], WIKI_TOKEN_HEADING, $nDepth);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize horizontal rule
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeHorizontalRule(&$aMatches) {
-        return $this->tokenize(false, WIKI_TOKEN_HR) . $aMatches[1];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=&lt;br&gt;=Ui',
-            array(&$this, 'tokenizeBreak'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize break
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeBreak(&$aMatches) {
-        return $this->tokenize(false, WIKI_TOKEN_BREAK);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=&lt;sub&gt;(.*)&lt;/sub&gt;=Ui',
-            array(&$this, 'tokenizeSub'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize sub
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.3
-     */
-    protected function tokenizeSub(&$aMatches) {
-        return $this->tokenize($aMatches[1], WIKI_TOKEN_SUB);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=&lt;sup&gt;(.*)&lt;/sup&gt;=Ui',
-            array(&$this, 'tokenizeSup'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize sup
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.3
-     */
-    protected function tokenizeSup(&$aMatches) {
-        return $this->tokenize($aMatches[1], WIKI_TOKEN_SUP);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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((\*|#) .*\n)(?! {0,}(\*|#))=Us',
-            array(&$this, 'tokenizeList'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize list
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeList(&$aMatches) {
-        return "\n".$this->tokenize($aMatches[1], WIKI_TOKEN_LIST)."\n";
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Create list
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function createList(&$sStr) {
-
-        // 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']  = $aList[3][$i];
-            $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
-     */
-    protected function buildList(&$i, &$nDepth) {
-        $sStr = '';
-
-        // Iterate though all list-entries
-        while (isset($this->aList[$i]['DEPTH'])
-               && $this->aList[$i]['DEPTH'] == $nDepth) {
-
-            $sStr .= '<li>';
-            $sStr .=    $this->aList[$i]['TEXT'];
-
-            $i++;
-
-            // Recurse (indent) if list-entry is nested
-            if (isset($this->aList[$i]['DEPTH'])) {
-
-                if ($this->aList[$i]['DEPTH'] > $nDepth) {
-
-                    // Correct items that are intended too deep
-                    $this->aList[$i]['DEPTH'] = $nDepth + 1;
-                
-                    $sListType = $this->aList[$i]['TYPE'] == '*'
-                                  ? 'ul'
-                                  : 'ol';
-
-                    $sStr .=  '<'.$sListType.'>';
-                    $sStr .=    $this->buildList(
-                                    $i,
-                                    $this->aList[$i]['DEPTH']
-                                );
-                    $sStr .=  '</'.$sListType.'>';
-                }
-            }
-
-            $sStr .= '</li>';
-        }
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;table&gt;\n(.+)\n&lt;/table&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizeTable'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize table
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeTable(&$aMatches) {
-        return "\n"
-                  .$this->tokenize($aMatches[1], WIKI_TOKEN_SIMPLE_TABLE)
-               ."\n";
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;table +([^>]*)&gt;\n(.+)\n&lt;/table&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizeParamTable'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize param table
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeParamTable(&$aMatches) {
-        return "\n"
-                  .$this->tokenize(
-                      $aMatches[2], WIKI_TOKEN_PARAM_TABLE, $aMatches[1]
-                  )
-                ."\n";
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * &build table
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function &buildTable(&$sStr) {
-
-        $aRows = explode("\n", $sStr);
-        $aTable = array();
-
-        for ($i=0, $n=sizeof($aRows); $i<$n; $i++) {
-
-            // Row MUST start with pipe
-            if (strlen(trim($aRows[$i])) == 0 || $aRows[$i]{0} != '|') {
-                return '<tr valign="top"><td colspan="1">'.$sStr.'</td></tr>';
-            }
-
-            $aRows[$i] = substr($aRows[$i], 1);
-
-            // Row MAY have one (or more) trailing pipes
-            if (substr($aRows[$i], -1) == '|') {
-                $aRows[$i] = substr($aRows[$i], 0, -1);
-            }
-
-            // Mark and remember all <noop>s in table row
-            $this->aTblRowNoop = array();
-            $aRows[$i] = preg_replace_callback(
-                            '#(<noop>.*</noop>)#U',
-                            array(&$this, 'extractTableRowNoops'),
-                            $aRows[$i]
-                         );
-
-            // Exchange column delemiter
-            $aRows[$i] = str_replace('|', "\x02", $aRows[$i]);
-
-            // Restore <noop>s in table row
-            for ($j=0, $m=sizeof($this->aTblRowNoop); $j<$m; $j++) {
-                $aRows[$i] = preg_replace(
-                                "#\x01#",
-                                $this->aTblRowNoop[$j],
-                                $aRows[$i],
-                                1
-                             );
-            }
-
-            // Divide columns
-            $aCols = explode("\x02", $aRows[$i]);
-
-            $nSpan = 0;
-            $sRow = '';
-
-            for ($j=0, $m=sizeof($aCols); $j<$m; $j++) {
-
-                if (strlen($aCols[$j]) == 0) {
-                    $nSpan++;
-                    if ($j < $m-1) {
-                        continue;
-                    }
-                }
-
-                if ($nSpan) {
-                    $sRow .= '<td colspan="'.($nSpan+1).'">';
-                    $sRow .=   $aCols[$j];
-                    $sRow .= '</td>';
-                    $nSpan = 0;
-                    continue;
-                }
-
-                $sRow .= '<td colspan="1">'.$aCols[$j].'</td>';
-            }
-
-            $aTable[] = '<tr valign="top">'
-                          .$sRow
-                        .'</tr>';
-        }
-
-        $sTable = join('', $aTable);
-        return $sTable;
-    }
-
-    // --------------------------------------------------------------------
-
-    private function extractTableRowNoops(&$aMatches) {
-        $this->aTblRowNoop[] = $aMatches[1];
-        return "\x01";
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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(
-            '=^&lt;q&gt;(.+)\n+&lt;/q&gt;(\s|$)=Umsi',
-            array(&$this, 'tokenizeQuote'),
-            $sStr
-        );
-
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Tokenize quote
-     *
-     * @access  protected
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     */
-    protected function tokenizeQuote(&$aMatches) {
-        return $this->tokenize($aMatches[1], WIKI_TOKEN_QUOTE)
-               .$aMatches[2];
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * 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
 

File [changed]: class.WikiReverseParser.php
Url: http://cowiki.tigris.org/source/browse/cowiki/includes/cowiki/class/parse/class.WikiReverseParser.php?r1=1.17&r2=1.18
Delta lines:  +612 -722
-----------------------
--- class.WikiReverseParser.php	9 Apr 2005 23:34:07 -0000	1.17
+++ class.WikiReverseParser.php	29 May 2005 15:39:35 -0000	1.18
@@ -2,7 +2,7 @@
 
 /**
  *
- * $Id: class.WikiReverseParser.php,v 1.17 2005/04/09 23:34:07 dgorski Exp $
+ * $Id: class.WikiReverseParser.php,v 1.18 2005/05/29 15:39:35 cmarble 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
@@ -17,7 +17,7 @@
  * @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.17 $
+ * @version     $Revision: 1.18 $
  *
  */
 
@@ -30,12 +30,31 @@
  *
  * @author      Daniel T. Gorski, <[email protected]>
  * @since       coWiki 0.3.0
+ * @author      Archie Campbell <[email protected]>
+ * @since       coWiki 0.4.0
  *
  * @todo        [D11N]  Complete documentation
  */
 class WikiReverseParser extends Object {
     protected static
-        $Instance = null;
+        $Instance = null,
+	$Context = null,
+	$DocDAO = null;
+    private
+        $rParser = null;
+    private
+    $ignoreToc = false,
+        $aList = array(),
+      $nDepth = 0,
+      $aType = array(),
+      $aTable = array(),
+      //      $nTable = null;
+      $sLink = array(),
+      $fLink = array(),
+        $aState = array(0),
+        $sContent = '',
+        $aRow = array(),
+      $nListIndent = null;
 
     // --------------------------------------------------------------------
 
@@ -72,104 +91,11 @@
      *
      * @todo    [D11N]  Check description
      */
-    protected function __construct() {}
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Parse
-     *
-     * @access  public
-     * @param   string
-     * @return  mixed
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
-     */
-    public function parse($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);
-
-        // Mark all newlines in <pre>, <code>, <posting> and <rem> as tabs
-        $sStr = preg_replace_callback(
-            '=(<pre>.*</pre>)=Usi',
-            array(&$this, 'newlineToTab'),
-            $sStr
-        );
-        $sStr = preg_replace_callback(
-            '=(<code>.*</code>)=Usi',
-            array(&$this, 'newlineToTab'),
-            $sStr
-        );
-        $sStr = preg_replace_callback(
-            '=(<posting>.*</posting>)=Usi',
-            array(&$this, 'newlineToTab'),
-            $sStr
-        );
-        $sStr = preg_replace_callback(
-            '=(<rem>.*</rem>)=Usi',
-            array(&$this, 'newlineToTab'),
-            $sStr
-        );
-
-        // Remove all newlines
-        $sStr = str_replace("\n", '', $sStr);
-        // Restore newlines in <pre>s, <code>s and <posting>s
-        $sStr = str_replace("\t", "\n", $sStr);
-
-        // ---
-
-        $sStr = $this->processToc($sStr);
-        $sStr = $this->processNoop($sStr);
-        $sStr = $this->processRemark($sStr);
-        $sStr = $this->processPre($sStr);
-        $sStr = $this->processCode($sStr);
-        $sStr = $this->processPosting($sStr);
-        $sStr = $this->processParagraph($sStr);
-        $sStr = $this->processHeading($sStr);
-        $sStr = $this->processEmphasis($sStr);
-        $sStr = $this->processHorizontalRule($sStr);
-        $sStr = $this->processVariable($sStr);
-        $sStr = $this->processPlugin($sStr);
-        $sStr = $this->processLink($sStr);
-        $sStr = $this->processUri($sStr);
-        $sStr = $this->processBreak($sStr);
-        $sStr = $this->processSup($sStr);
-        $sStr = $this->processSub($sStr);
-        $sStr = $this->processList($sStr);
-        $sStr = $this->processTable($sStr);
-        $sStr = $this->processQuote($sStr);    // last one!
-
-        return trim(unescape($sStr));
+    protected function __construct() {
+	$this->Context = RuntimeContext::getInstance();
+        $this->DocDAO = $this->Context->getDocumentDAO();
     }
 
-    // --------------------------------------------------------------------
-
-    /**
-     * Newline to tab
-     *
-     * @access  protected
-     * @param   array
-     * @return  mixed
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
-     * @todo    [FIX]   THIS METHOD DO NOT COVER ALL POSSIBLE OCCURANCES OF DELIMITERS
-     */
-    protected function newlineToTab(&$aMatches) {
-        return str_replace("\n", "\t", $aMatches[1]);
-    }
 
     // --------------------------------------------------------------------
 
@@ -214,672 +140,623 @@
     // --------------------------------------------------------------------
 
     /**
-     * Process toc
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
+     * Helper functions
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
-     * @todo    [D11N]  Check description
      */
-    protected function processToc(&$sStr) {
-        $sStr = preg_replace(
-            '=<toc>(.*)</toc>=Ums',
-            "\n&lt;toc&gt;\n",
-            $sStr
-        );
-        return $sStr;
+    protected function appendRow($sStr){
+	array_push($this->aRow,$sStr);
+	$this->clearContent();
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process noop
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processNoop(&$sStr) {
-        $sStr = preg_replace(
-            '=<noop>(.*)</noop>=Ums',
-            "&lt;noop&gt;\\1&lt;/noop&gt;",
-            $sStr
-        );
-        return $sStr;
+    protected function clearContent(){
+	$this->sContent = '';
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process remark
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processRemark(&$sStr) {
-        $sStr = preg_replace(
-            '=<rem>(.*)</rem>=Ums',
-            "&lt;rem&gt;\\1&lt;/rem&gt;",
-            $sStr
-        );
-        return $sStr;
+    protected function clearRow() {
+        if(trim($this->sContent)!=''){
+	    $this->appendRow($this->sContent);
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process pre
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processPre(&$sStr) {
-        $sStr = preg_replace(
-            '=<pre>(.*)</pre>=Ums',
-            "\n&lt;pre&gt;\\1&lt;/pre&gt;\n",
-            $sStr
-        );
-        return $sStr;
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process code
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processCode(&$sStr) {
-        $sStr = preg_replace(
-            '=<code>(.*)</code>=Ums',
-            "\n&lt;code&gt;\\1&lt;/code&gt;\n",
-            $sStr
-        );
-        return $sStr;
+    protected function textListItem( $sData ) {
+	$this->aList[sizeof($this->aList)-1]['TEXT'] .= $sData;
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process posting
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processPosting(&$sStr) {
-        $sStr = preg_replace(
-            '=<posting>(.*)</posting>=Ums',
-            "\n&lt;posting&gt;\\1&lt;/posting&gt;\n",
-            $sStr
-        );
-        return $sStr;
+    protected function pushTableItem( $sData ) {
+	array_push($this->aTable[sizeof($this->aTable)-1],$sData);
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process paragraph
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processParagraph(&$sStr) {
-        $sStr = preg_replace(
-            '=<p>(.*)</p>=Ums',
-            "\n\\1\n",
-            $sStr
-        );
-        return $sStr;
+    protected function textTableItem( $sData ) {
+	if(trim($this->sContent)!='')
+	{ 
+	    $sStr = $this->sContent;
+	    $this->clearContent();
+	    $this->textTableItem(trim($sStr));
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process heading
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processHeading(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<h([1-6])>(.*)</h\1>=Ums',
-            array(&$this, 'buildHeading'),
-            $sStr
-        );
-        return $sStr;
+	$a =& $this->aTable[sizeof($this->aTable)-1];
+	$a[sizeof($a)-1] .= $sData;
     }
 
-    // --------------------------------------------------------------------
-
     /**
-     * Build heading
+     * Parse
      *
-     * @access  protected
-     * @param   array
+     * @access  public
+     * @param   string
      * @return  mixed
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
-     * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
      */
-    protected function buildHeading(&$aMatches) {
-        return "\n"
-               .str_repeat('+', $aMatches[1])
-               .' '
-               .$aMatches[2]
-               ."\n";
-    }
+    public function parse($sStr) {
 
-    // --------------------------------------------------------------------
+      //return $this->dummy($sStr);
 
-    /**
-     * Process emphasis
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processEmphasis(&$sStr) {
-        $sStr = preg_replace(
-            '=<em>(.*)</em>=Ums',
-            '/\1/',
-            $sStr
-        );
-
-        $sStr = preg_replace(
-            '=<strong>(.*)</strong>=Ums',
-            '*\1*',
-            $sStr
-        );
-
-        $sStr = preg_replace(
-            '=<tt>(.*)</tt>=Ums',
-            '=\1=',
-            $sStr
-        );
-        return $sStr;
+        if (!function_exists('xml_parser_create')) {
+            return false;
     }
 
-    // --------------------------------------------------------------------
+        $this->rParser = @xml_parser_create();
 
-    /**
-     * Process horizontal rule
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processHorizontalRule(&$sStr) {
-        $sStr = preg_replace(
-            '=<hr/>=Ums',
-            "\n---\n",
-            $sStr
-        );
-        return $sStr;
+        if (!$this->rParser) {
+            return false;
     }
 
-    // --------------------------------------------------------------------
+        xml_parser_set_option($this->rParser, XML_OPTION_CASE_FOLDING, 0);
+        xml_parser_set_option($this->rParser, XML_OPTION_SKIP_WHITE, 1);
 
-    /**
-     * Process variable
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processVariable(&$sStr) {
-        $sStr = preg_replace(
-            '=<var name\="(.*)"/>=Ums',
-            '%\1%',
-            $sStr
-        );
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
+        // Replace possible tabulators
+        $sStr = str_replace("\t", '    ', $sStr);
 
-    /**
-     * Process link
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processLink(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<link strref\="(.*)">(.*)</link>=Ums',
-            array(&$this, 'buildStrRefLink'),
-            $sStr
-        );
-        return $sStr;
-    }
+        // Replace possible \r\n or \n\r with \n
+        $sStr = str_replace("\r\n", "\n", $sStr);
+        $sStr = str_replace("\n\r", "\n", $sStr);
 
-    // --------------------------------------------------------------------
+	// Prepare XML, add root element
+	$sStr = '<document>' . $sStr . '</document>';
 
-    /**
-     * Build str ref link
-     *
-     * @access  protected
-     * @param   array
-     * @return  mixed
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function buildStrRefLink(&$aMatches) {
-        if ($aMatches[1] == $aMatches[2] || trim($aMatches[2]) == '') {
-            return '((' . $this->noopDelimiters($aMatches[1]) . '))';
+	xml_set_default_handler( $this->rParser, 
+				array(&$this,'xml_default_handler') );
+	xml_set_element_handler( $this->rParser,
+				array(&$this,'xml_start_handler'),
+				array(&$this,'xml_end_handler') );
+	xml_set_character_data_handler( $this->rParser,
+				array(&$this,'xml_cdata_handler') );
+
+	if(!xml_parse( $this->rParser, $sStr )){
+	  echo xml_error_string(xml_get_error_code($this->rParser));
+	}
+
+	return implode("\n",$this->aRow);
+
+    }
+
+    /**
+     * xml element start handler
+     *
+     * @access  protected
+     *
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
+     *
+     */
+    protected function xml_start_handler( &$parser, $sName, $aAttrib ) {
+        $debug = false;
+	if($debug) { 
+	  echo "start ".$sName." ".implode("|",$aAttrib)."\n";
+	}
+        if($this->ignoreToc){ return; }
+        switch($sName){
+	case 'document':
+	    $this->aRow = array();
+	    break;
+	case 'toc':
+	    $this->clearRow();
+	    $this->ignoreToc = true;
+	    break;
+	case 'plugin':
+	    $sStr = '<plugin '.$aAttrib['name'];
+	    if(sizeof($aAttrib)>1){
+	        foreach($aAttrib as $attr => $value){
+		    if($attr != 'name'){
+		        $sStr .= ' '.$attr.'="'.$value.'"';
+		    }
+		}
+	    }
+	    $sStr .= '>';
+	    $this->sContent .= $sStr;
+  	    break;
+	//table syntax
+	case 'table':
+	    $this->clearRow();
+	    array_push($this->aState,6);/*WRP_TABL*/
+	    array_push($this->aTable,array($aAttrib));//[][0]
+	    break;
+	case 'caption':
+	    array_push($this->aState,7);/*WRP_CAPN*/
+	    $this->clearContent();
+	    break;
+	case 'tr':
+   	    array_push($this->aState,8);/*WRP_TROW*/
+	    $sStr = '|-';
+	    if(sizeof($aAttrib)){
+		$first = true;
+  	        foreach($aAttrib as $attr => $value){
+		    $sStr .= ($first?'':' ').$attr.'="'.$value.'"';
+		    $first = false;
+ 		}
+	    }
+	    $this->pushTableItem($sStr);
+	    break;
+	case 'th':
+	    array_push($this->aState,9);/*WRP_THED*/
+	    $sStr = '!';
+	    if(sizeof($aAttrib)){
+		$first = true;
+  	        foreach($aAttrib as $attr => $value){
+		    $sStr .= ($first?'':' ').$attr.'="'.$value.'"';
+		    $first = false;
         }
-
-        return '((' . $this->noopDelimiters($aMatches[1]) . ')'
-                .'(' . $aMatches[2] . '))';
+		$sStr .= '|';
+	    } else {
+	        $sStr .= ' ';
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process uri
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processUri(&$sStr) {
-        $sStr = preg_replace(
-            '=<uri strref\="(.*)"/>=Ums',
-            '\1',
-            $sStr
-        );
-        return $sStr;
+	    $this->pushTableItem($sStr);
+	    break;
+	case 'td':
+	    array_push($this->aState,10);/*WRP_TCEL*/
+	    $sStr = '|';
+	    if(sizeof($aAttrib)){
+		$first = true;
+  	        foreach($aAttrib as $attr => $value){
+		    switch($attr)
+		    {
+		    case 'origin':
+			if( $value == 'doubled' ) {
+			    $sStr .= '| ';
+			}
+			break;
+		    default:
+			$sStr .= ($first?'':' ').$attr.'="'.$value.'"';
+			$first = false;
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process break
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processBreak(&$sStr) {
-        $sStr = preg_replace(
-            '=<br/>=Ui',
-            "&lt;br&gt;",
-            $sStr
-        );
-        return $sStr;
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process plugin
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processPlugin(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<plugin name\="([^"]+)"(.*)/>=Ums',
-            array(&$this, 'buildPlugin'),
-            $sStr
-        );
-        return $sStr;
+		if($aAttrib['origin'] != 'doubled') {
+		    $sStr .= '| ';
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Build plugin
-     *
-     * @access  protected
-     * @param   array
-     * @return  mixed
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function buildPlugin(&$aMatches) {
-        if ($aMatches[2] == '') {
-            return '&lt;plugin '.$aMatches[1].'&gt;';
         } else {
-            return '&lt;plugin '.$aMatches[1].' '.trim($aMatches[2]).'&gt;';
-        }
+	        $sStr .= ' ';
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process list
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processList(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<list>(.*)</list>=Ums',
-            array(&$this, 'buildList'),
-            $sStr
-        );
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Build list
-     *
-     * @access  protected
-     * @param   array
-     * @return  mixed
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
-     */
-    protected function buildList(&$aMatches) {
-        // Remove closing </li>
-        $sStr = preg_replace('=</li>=i', '', $aMatches[1]);
-        $this->nListDepth = 0;
-
-        $sList = '';
-        return "\n" . $this->buildMixedList($sStr, $sList);
+	    $this->pushTableItem($sStr);
+	    break;
+	//list syntax
+	case 'list':
+	  //$this->clearRow();
+	    array_push($this->aState,1);/*WRP_LIST*/
+	    break;
+	case 'ol':
+	case 'ul': 
+	    $this->nDepth++;
+	    array_push($this->aType,$sName);
+	    break;
+	case 'li':
+	    switch(end($this->aState)){
+	    case 1:
+		array_push( $this->aList, 
+			    array(  'DEPTH'=>$this->nDepth,
+				    'TYPE'=>(end($this->aType)=='ol'?
+							         "#":"*"),
+				    'TEXT'=>''  ));
+		break;
+	    default:
+	      echo $sName . "insanity";
+	      exit;
+	    }
+	    $this->clearContent();
+	    break;
+        //paragraphs
+	case 'p':
+	    $this->clearRow();
+	    break;
+        //justification
+	case 'left':
+	case 'right':
+	case 'center':
+	    array_push($this->aState,16);
+	    $this->sContent .= '<'.$sName.'>';
+	    break;
+        //pre, code, posting, quote, remark, noop
+	case 'pre':
+	case 'code':
+	case 'posting':
+	case 'q':
+	case 'rem':
+	case 'noop':
+	    $this->sContent .= '<'.$sName.'>';
+	    array_push($this->aState,4);
+	    break;
+	//line-break
+	case 'br':
+	    break;
+        //variables
+	case 'var':
+	    $this->sContent .= "%".$aAttrib['name']."%";
+	    break;
+	//headings
+	case 'h1':
+	case 'h2':
+	case 'h3':
+	case 'h4':
+	case 'h5':
+	case 'h6':
+	    array_push($this->aState,3);
+            break;
+	//wiki links
+	case 'uri':
+	    $this->sContent .= $aAttrib['strref'];
+	    break;
+	case 'link':
+	    array_push($this->aState,2);
+	    if( isset($aAttrib['strref']) ) {
+		$this->fLink = array(&$this, 'buildStrRefLink');
+		$this->sLink[1] = $aAttrib['strref'];
+	    }else if( isset($aAttrib['href']) ) {
+		$this->fLink = array(&$this, 'buildHyperRefLink');
+		$this->sLink[1] = $aAttrib['href'];
+	    }else if( isset($aAttrib['idref']) ) {
+		$this->fLink = array(&$this, 'buildIdRefLink');
+		$this->sLink[1] = $aAttrib['idref'];
+	    }
+	    $this->sLink[0] = $aAttrib['origin'];
+	    $this->sLink[2] = '';
+    	    break;
+	//subscript & superscript
+	case 'sub':
+	case 'sup':
+	    $this->sContent .= '<'.$sName.'>';
+	    break;
+	//horizontal rule
+	case 'hr':break;
+	//emphasis (strike,tt,b,u,i)
+	//WRP_ST WRP_TT WRP_B WRP_U WRP_I
+	//11     12     13    14    15 <- html
+	//17     18     19    20    21 <- wiki
+	case 'strike':
+	    if(isset($aAttrib['origin']) && $aAttrib['origin']=='html'){
+	        array_push($this->aState,11);
+	        $this->sContent .= "<strike>";
+	    }else{
+	        array_push($this->aState,17);
+		$this->sContent .= "--";
+	    }
+	    break;
+	case 'tt':
+	    if(isset($aAttrib['origin']) && $aAttrib['origin']=='html'){
+	        array_push($this->aState,12);
+	        $this->sContent .= "<tt>";
+	    }else{
+	        array_push($this->aState,18);
+		$this->sContent .= "==";
+	    }
+	    break;
+	case 'b':
+	    if(isset($aAttrib['origin']) && $aAttrib['origin']=='html'){
+	        array_push($this->aState,13);
+	        $this->sContent .= "<b>";
+	    }else{
+	        array_push($this->aState,19);
+		$this->sContent .= "**";
+	    }
+	    break;
+	case 'u':
+	    if(isset($aAttrib['origin']) && $aAttrib['origin']=='html'){
+	        array_push($this->aState,14);
+	        $this->sContent .= "<u>";
+	    }else{
+	        array_push($this->aState,20);
+		$this->sContent .= "__";
+	    }
+	    break;
+	case 'i':
+	    if(isset($aAttrib['origin']) && $aAttrib['origin']=='html'){
+	        array_push($this->aState,15);
+	        $this->sContent .= "<i>";
+	    }else{
+	        array_push($this->aState,21);
+		$this->sContent .= "//";
+	    }
+	    break;
+	default:
+            echo $sName . " start" . "\n";
+	}
+    }
+
+    /**
+     * xml element end handler
+     *
+     * @access  protected
+     *
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
+     *
+     */
+    protected function xml_end_handler( &$parser, $sName ) {
+        $debug = false;
+	if($debug) { 
+	  echo "end ".$sName."\n";
+	}
+        if($this->ignoreToc){
+	    if($sName == 'toc'){
+		$this->appendRow("<toc>");
+		$this->ignoreToc = false;
+		return;
+	    }
+	}
+        switch($sName){
+	case 'document': 
+	    $this->clearRow();
+	    break;
+	case 'plugin':
+	    break;
+	//table syntax
+	case 'table':
+	    $this->clearContent();
+	    if(array_pop($this->aState)!=6) {
+	        echo "insanity in table";
+	        exit;
+	    }
+	    $aTable = array_pop($this->aTable);
+	    $sStr = '<table';
+	    foreach($aTable[0] as $attr => $value ){
+	        $sStr .= " ".$attr.'="'.$value.'"';
+	    }
+	    $sStr .= (sizeof($aTable)==1?'/>':'>');
+	    if($debug) { echo $sStr." ".(sizeof($aTable)-1)."\n"; }
+	    if($debug) { echo $sStr." ".implode(",",$aTable)."\n"; }
+	    $this->appendRow( $sStr );
+	    for ($i = 1; $i < sizeof($aTable) ; $i++){
+	        if(trim($aTable[$i]) != '') {
+		    if($debug) { echo $aTable[$i]."\n"; }
+		    $this->appendRow( trim($aTable[$i]) );
+		}
+	    }
+	    $this->appendRow('</table>');
+	    break;
+        //caption
+	case 'caption':
+            if(array_pop($this->aState)!=7) {
+	        echo "insanity in caption";
+	        exit;
+	    }
+	    $this->pushTableItem( "|+ ".$this->sContent );
+	    $this->clearContent();
+	    break;
+	case 'tr':
+	    if(array_pop($this->aState)!=8) {
+  	        echo "insanity in trow";
+	        exit;
+	    }
+	    $this->textTableItem('');
+  	    break;
+	case 'th':
+	    if(array_pop($this->aState)!=9) {
+	        echo "insanity in thead";
+		exit;
+	    }
+	    $this->textTableItem('');
+	    break;
+	case 'td':
+	    if(array_pop($this->aState)!=10) {
+	        echo "insanity in tcell";
+		exit;
+	    }
+	    $this->textTableItem('');
+	    break;
+	//list syntax
+	case 'list':
+	    if(array_pop($this->aState)!=1) {
+	        echo "insanity in list";
+	        exit;
+	    };
+	    foreach($this->aList as $li){
+		$sStr = str_repeat($li['TYPE'],$li['DEPTH']).
+		  " ".$li['TEXT'];
+	      switch(end($this->aState)){
+	      case 9:
+	      case 10:
+		  $this->pushTableItem( $sStr );
+		  break;
+	      default:
+		  $this->appendRow( $sStr );
+	      }
+	    }
+	    $this->clearContent();
+	    $this->aList = array();
+	    break;
+	case 'ol':
+	case 'ul':
+	    $this->nDepth--;
+	    array_pop($this->aType);
+	    break;
+	case 'li':
+	    $this->clearContent();
+	    break;
+        //paragraphs
+	case 'p':
+	    $this->appendRow( $this->sContent );
+	    break;
+        //justification
+	case 'left':
+	case 'right':
+	case 'center':
+	    if(array_pop($this->aState)!=16){
+	        echo "insanity in just";
+	        exit;
+	    }
+	    $this->sContent .= '</'.$sName.'>';
+	    break;
+        //pre, code, posting, quote, remark, noop
+	case 'pre':
+	case 'code':
+	case 'posting':
+	case 'q':
+	case 'rem':
+	case 'noop':
+	    $this->sContent .= '</'.$sName.'>';
+	    if(array_pop($this->aState)!=4){
+	        echo "insanity in pcpq";
+	        exit;
+	    }
+	    /*
+	    if(end($this->aState)!=1){
+	        $this->clearRow();
+	    }
+	    */
+	    break;
+	//line-break
+	case 'br':
+	    $this->sContent .= '<br/>';
+	    break;
+        //variables
+	case 'var':
+	    break;
+	//headings
+	case 'h1':
+	case 'h2':
+	case 'h3':
+	case 'h4':
+	case 'h5':
+	case 'h6':
+	    $this->appendRow( str_repeat("+",substr($sName,1,1)).
+				" ". $this->sContent);
+	    array_pop( $this->aState );
+	    break;
+	//wiki links
+	case 'uri':
+	    break;
+	case 'link':
+	    if(array_pop($this->aState)!=2){
+	        echo "insanity in link";
+		exit;
+	    }
+	    $sStr = call_user_func($this->fLink, $this->sLink);
+            $this->sContent .= $sStr;
+	    break;
+	//subscript & superscript
+	case 'sub':
+	case 'sup':
+	    $this->sContent .= '</'.$sName.'>';
+	    break;
+	//horizontal rule
+	case 'hr':
+	    $this->appendRow("---");
+	    break;
+	//emphasis (strike,tt,b,u,i)
+	case 'strike':
+	    if(array_pop($this->aState)==11){
+		$this->sContent .= "</strike>";
+	    } else {
+	        $this->sContent .= "--";
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Build mixed list
-     *
-     * @access  protected
-     * @param   string
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function buildMixedList(&$sStr, &$sList) {
-        static $aListType = array();
-
-        $bMatch = preg_match('=<([^>]*)>([^<]*)(.*)=sS', $sStr, $aMatches);
-
-        if ($bMatch) {
-
-            if ($aMatches[1] == 'ul') {
-                $sListTag = '*';
-                $aListType[$this->nListDepth] = '*';
-                $this->nListDepth++;
-                $this->buildMixedList($aMatches[3], $sList);
+	    break;
+	case 'tt':
+	    if(array_pop($this->aState)==12){
+		$this->sContent .= "</tt>";
+	    } else {
+	        $this->sContent .= "==";
             }
-
-            if ($aMatches[1] == 'ol') {
-                $aListType[$this->nListDepth] = '#';
-                $this->nListDepth++;
-                $this->buildMixedList($aMatches[3], $sList);
+	    break;
+	case 'b':
+	    if(array_pop($this->aState)==13){
+		$this->sContent .= "</b>";
+	    } else {
+	        $this->sContent .= "**";
             }
-
-            if ($aMatches[1] == 'li') {
-                $sList .= str_repeat(' ',  $this->nListDepth - 1)
-                          . $aListType[$this->nListDepth - 1] . ' '
-                          . str_replace("\n", '', $aMatches[2]) . "\n";
-                $this->buildMixedList($aMatches[3], $sList);
+	    break;
+	case 'u':
+	    if(array_pop($this->aState)==14){
+		$this->sContent .= "</u>";
+	    } else {
+	        $this->sContent .= "__";
             }
-
-            if ($aMatches[1] == '/ul') {
-                $this->nListDepth--;
-                $this->buildMixedList($aMatches[3], $sList);
+	    break;
+	case 'i':
+	    if(array_pop($this->aState)==15){
+		$this->sContent .= "</i>";
+	    } else {
+	        $this->sContent .= "//";
             }
-
-            if ($aMatches[1] == '/ol') {
-                $this->nListDepth--;
-                $this->buildMixedList($aMatches[3], $sList);
+	    break;
+	default:
+	    echo $sName . " end" . "\n";
             }
+ 	if(end($this->aState)==1){
+ 	    $this->textListItem($this->sContent);
+	    $this->clearContent();
         }
-
-        return  $sList;
     }
 
-    // --------------------------------------------------------------------
-
     /**
-     * Process table
+     * xml element default handler
      *
      * @access  protected
-     * @param   string
-     * @return  string
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
-     * @todo    [D11N]  Check description
      */
-    protected function processTable(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<table([^>]*)>(.*)</table>=Ums',
-            array(&$this, 'buildTable'),
-            $sStr
-        );
-
-        return $sStr;
+    protected function xml_default_handler( &$parser, $sData ) {
+        if(!$this->ignoreToc){
+            $this->sContent .= html_entity_decode($sData);
+	}
     }
-
-    // --------------------------------------------------------------------
 
     /**
-     * Build table
+     * xml cdata handler
      *
      * @access  protected
-     * @param   array
-     * @return  mixed
      *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
+     * @author  Archie Campbell <[email protected]>
+     * @since   coWiki 0.4.0
      *
-     * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
      */
-    protected function buildTable(&$aMatches) {
-        $sStr = "\n&lt;table".$aMatches[1]."&gt;\n";
-
-        // Isolate rows
-        preg_match_all(
-            '=<tr[^>]*>(.+)</tr>=USs',
-            $aMatches[2],
-            $aRows
-        );
-
-        // Treat cells
-        for ($i=0, $n=sizeof($aRows[1]); $i<$n; $i++) {
-            preg_match_all(
-                '=<td colspan\="([0-9]+)">(.*)</td>=USs',
-                $aRows[1][$i],
-                $aCells
-            );
-
-            for ($j=0, $m=sizeof($aCells[1]); $j<$m; $j++) {
-                $sStr .= str_repeat('|', $aCells[1][$j]);
-                $sStr .= $aCells[2][$j];
+    protected function xml_cdata_handler( &$parser, $sData ) {
+        $debug = false;
+	if( $debug ) { 
+	    echo "cdata ".$sData."\n";
             }
-
-            $sStr .= "\n";
+        if($this->ignoreToc){ return; }
+        if( $sData != '' ) {
+	    switch( end($this->aState) ){
+	    case 2:/*WRP_LINK*/
+	        $this->sLink[2] .= trim($sData);
+	        break;
+	    case 4:/*WRP_PCPQ*/
+		$this->sContent .= /*trim*/($sData);
+		break;
+	    case 1:/*WRP_LIST*/
+	        $this->textListItem($sData);
+	        break;
+	    case 9:/*WRP_THED*/
+	    case 10:/*WRP_TCEL*/
+	        $this->textTableItem($sData);
+		break;
+	    case 3:/*WRP_HEAD*/
+	    case 7:/*WRP_CAPT*/
+	    default:/*text*/
+	        $this->sContent .= $sData;
         }
-
-        return $sStr . "&lt;/table&gt;\n";
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process sup
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.4
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processSup(&$sStr) {
-        $sStr = preg_replace(
-            '=<sup>(.*)</sup>=Ums',
-            "&lt;sup&gt;\\1&lt;/sup&gt;",
-            $sStr
-        );
-        return $sStr;
     }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process sub
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.4
-     *
-     * @todo    [D11N]  Check description
-     */
-    protected function processSub(&$sStr) {
-        $sStr = preg_replace(
-            '=<sub>(.*)</sub>=Ums',
-            "&lt;sub&gt;\\1&lt;/sub&gt;",
-            $sStr
-        );
-        return $sStr;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Process quote
-     *
-     * @access  protected
-     * @param   string
-     * @return  string
-     *
-     * @author  Daniel T. Gorski, <[email protected]>
-     * @since   coWiki 0.3.0
-     *
-     * @todo    [D11N]  Check description
+  /*
+	    case 11: emphases
+	    case 12:
+	    case 13:
+	    case 14:
+	    case 15:
      */
-    protected function processQuote(&$sStr) {
-        $sStr = preg_replace_callback(
-            '=<q>(.*)</q>=Ums',
-            array(&$this, 'buildQuote'),
-            $sStr
-        );
-        return $sStr;
-    }
-
     // --------------------------------------------------------------------
 
     /**
-     * Build quote
+     * Build str ref link
      *
      * @access  protected
      * @param   array
@@ -889,10 +766,23 @@
      * @since   coWiki 0.3.0
      *
      * @todo    [D11N]  Check description
-     * @todo    [D11N]  Check return type
      */
-    protected function buildQuote(&$aMatches) {
-        return "\n&lt;q&gt;\n".trim($aMatches[1])."\n&lt;/q&gt;\n";
+    protected function buildStrRefLink(&$aMatches) {
+        if ($aMatches[1] == $aMatches[2] || trim($aMatches[2]) == '') {
+	    if( $aMatches[0] == 'square' ) {
+		return '[[' . $this->noopDelimiters($aMatches[1]) . ']]';
+	    } else {
+		return '((' . $this->noopDelimiters($aMatches[1]) . '))';
+	    }
+        }
+
+	if($aMatches[0] == 'square' ) {
+	    return '[[' . $this->noopDelimiters($aMatches[1]) . ']'
+                .'[' . $aMatches[2] . ']]';
+	} else {
+	    return '((' . $this->noopDelimiters($aMatches[1]) . ')'
+                .'(' . $aMatches[2] . '))';
+	}
     }
 
 } // of class