diff --git a/inc/html.php b/inc/html.php
index f9acd7325173c9e73043db2f13e68efca62fcb45..46336b68dfa47960cd8cd7a869294b516151bdda 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -647,6 +647,9 @@ function html_li_default($item){
  * print the <li> tag. Both user function need to accept
  * a single item.
  *
+ * Both user functions can be given as array to point to
+ * a member of an object.
+ *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function html_buildlist($data,$class,$func,$lifunc='html_li_default'){
@@ -678,9 +681,17 @@ function html_buildlist($data,$class,$func,$lifunc='html_li_default'){
     $level = $item['level'];
 
     //print item
-    $ret .= $lifunc($item); //user function
+    if(is_array($lifunc)){
+      $ret .= $lifunc[0]->$lifunc[1]($item); //user object method
+    }else{
+      $ret .= $lifunc($item); //user function
+    }
     $ret .= '<div class="li">';
+    if(is_array($func)){
+      $ret .= $func[0]->$func[1]($item); //user object method
+    }else{
     $ret .= $func($item); //user function
+    }
     $ret .= '</div>';
   }
 
diff --git a/inc/parser/handler.php b/inc/parser/handler.php
index 3609e23a85df94a72e782640cb4b55707d759a5c..4653cf603264729ba9a0fe0a2542f6458863b858 100644
--- a/inc/parser/handler.php
+++ b/inc/parser/handler.php
@@ -37,10 +37,12 @@ class Doku_Handler {
             $this->calls = $B->process($this->calls);
         }
 
+/** FIXME deprecated
         if ( $this->meta['toc'] ) {
             $T = & new Doku_Handler_Toc();
             $this->calls = $T->process($this->calls);
         }
+*/
 
         array_unshift($this->calls,array('document_start',array(),0));
         $last_call = end($this->calls);
@@ -1495,6 +1497,9 @@ class Doku_Handler_Block {
 }
 
 //------------------------------------------------------------------------
+
+/** FIXME deprecated
+
 define('DOKU_TOC_OPEN',1);
 define('DOKU_TOCBRANCH_OPEN',2);
 define('DOKU_TOCITEM_OPEN',3);
@@ -1671,6 +1676,6 @@ class Doku_Handler_Toc {
     }
 
 }
-
+*/
 
 //Setup VIM: ex: et ts=4 enc=utf-8 :
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index 1c7343cda39490e222d7ba50d30d7f5b9cc8af2c..a54e5bcaba6747e31d4b81ab78aa781d8677460c 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -25,7 +25,10 @@ require_once DOKU_INC . 'inc/parser/renderer.php';
  */
 class Doku_Renderer_xhtml extends Doku_Renderer {
 
-    var $doc = '';
+    // @access public
+    var $doc = '';        // will contain the whole document
+    var $toc = array();   // will contain the Table of Contents
+
 
     var $headers = array();
 
@@ -42,6 +45,9 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
     var $store = '';
 
     function document_start() {
+        //reset some internals
+        $this->toc     = array();
+        $this->headers = array();
     }
 
     function document_end() {
@@ -81,8 +87,39 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
             }
             $this->doc .= '</div>'.DOKU_LF;
         }
+
+        // prepend the TOC
+        $this->doc = $this->render_TOC().$this->doc;
+    }
+
+    /**
+     * Return the TOC rendered to XHTML
+     *
+     * @author Andreas Gohr <andi@splitbrain.org>
+     */
+    function render_TOC(){
+        if(!count($this->toc)) return '';
+        global $lang;
+        $out  = '<div class="toc">'.DOKU_LF;
+        $out .= '<div class="tocheader toctoggle" id="toc__header">';
+        $out .= $lang['toc'];
+        $out .= '</div>'.DOKU_LF;
+        $out .= '<div id="toc__inside">'.DOKU_LF;
+        $out .= html_buildlist($this->toc,'toc',array($this,'_tocitem'));
+        $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
+        return $out;
     }
 
+    /**
+     * Callback for html_buildlist
+     */
+    function _tocitem($item){
+        return '<span class="li"><a href="#'.$item['hid'].'" class="toc">'.
+               $this->_xmlEntities($item['title']).'</a></span>';
+    }
+
+/** FIXME deprecated
+
     function toc_open() {
         global $lang;
         $this->doc .= '<div class="toc">'.DOKU_LF;
@@ -122,6 +159,8 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
         $this->doc .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
     }
 
+*/
+
     function header($text, $level, $pos) {
         global $conf;
         //handle section editing
@@ -132,7 +171,21 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
             $this->lastsec = $pos;
         }
 
-        $this->doc .= DOKU_LF.'<h'.$level.'><a name="'.$this->_headerToLink($text).'" id="'.$this->_headerToLink($text).'">';
+        // create a unique header id
+        $hid = $this->_headerToLink($text,'true');
+
+        //handle TOC
+//FIXME        if($this->meta['toc'] &&
+        if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){
+            // the TOC is one of our standard ul list arrays ;-)
+            $this->toc[] = array( 'hid'   => $hid,
+                                  'title' => $text,
+                                  'type'  => 'ul',
+                                  'level' => $level);
+        }
+
+        // write the header
+        $this->doc .= DOKU_LF.'<h'.$level.'><a name="'.$hid.'" id="'.$hid.'">';
         $this->doc .= $this->_xmlEntities($text);
         $this->doc .= "</a></h$level>".DOKU_LF;
     }
@@ -969,11 +1022,26 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
 
     /**
      * Creates a linkid from a headline
+     *
+     * @param string  $title   The headline title
+     * @param boolean $create  Create a new unique ID?
+     * @author Andreas Gohr <andi@splitbrain.org>
      */
-    function _headerToLink($title) {
-        $title = str_replace(':','',cleanID($title,true));
+    function _headerToLink($title,$create=false) {
+        $title = str_replace(':','',cleanID($title,true)); //force ASCII
         $title = ltrim($title,'0123456789');
         if(empty($title)) $title='section';
+
+        if($create){
+            // make sure tiles are unique
+            $num = '';
+            while(in_array($title.$num,$this->headers)){
+                ($num) ? $num = 1 : $num++;
+            }
+            $title = $title.$num;
+            $this->headers[] = $title;
+        }
+
         return $title;
     }