tags
   *
   * @param void
   * @return void
   */
  function handleTag_table() {
    if ($this->parser->isStartTag) {
      # check if upcoming table can be converted
      if ($this->keepHTML) {
        if (preg_match($this->tableLookaheadHeader, $this->parser->html, $matches)) {
          # header seems good, now check body
          # get align & number of cols
          preg_match_all('#| #si', $matches[0], $cols);
          $regEx = '';
          $i = 1;
          $aligns = array();
          foreach ($cols[2] as $align) {
            $align = strtolower($align);
            array_push($aligns, $align);
            if (empty($align)) {
              $align = 'left'; # default value
            }
            $td = '\s+align=("|\')'.$align.'\\'.$i;
            $i++;
            if ($align == 'left') {
              # look for empty align or left
              $td = '(?:'.$td.')?';
            }
            $td = ' | ';
            $regEx .= $td.$this->tdSubstitute;
          }
          $regEx = sprintf($this->tableLookaheadBody, $regEx);
          if (preg_match($regEx, $this->parser->html, $matches, null, strlen($matches[0]))) {
            # this is a markdownable table tag!
            $this->table = array(
              'rows' => array(),
              'col_widths' => array(),
              'aligns' => $aligns,
            );
            $this->row = 0;
          } else {
            # non markdownable table
            $this->handleTagToText();
          }
        } else {
          # non markdownable table
          $this->handleTagToText();
        }
      } else {
        $this->table = array(
          'rows' => array(),
          'col_widths' => array(),
          'aligns' => array(),
        );
        $this->row = 0;
      }
    } else {
      # finally build the table in Markdown Extra syntax
      $separator = array();
      # seperator with correct align identifikators
      foreach($this->table['aligns'] as $col => $align) {
        if (!$this->keepHTML && !isset($this->table['col_widths'][$col])) {
          break;
        }
        $left = ' ';
        $right = ' ';
        switch ($align) {
          case 'left':
            $left = ':';
            break;
          case 'center':
            $right = ':';
            $left = ':';
          case 'right':
            $right = ':';
            break;
        }
        array_push($separator, $left.str_repeat('-', $this->table['col_widths'][$col]).$right);
      }
      $separator = '|'.implode('|', $separator).'|';
      $rows = array();
      # add padding
      array_walk_recursive($this->table['rows'], array(&$this, 'alignTdContent'));
      $header = array_shift($this->table['rows']);
      array_push($rows, '| '.implode(' | ', $header).' |');
      array_push($rows, $separator);
      foreach ($this->table['rows'] as $row) {
        array_push($rows, '| '.implode(' | ', $row).' |');
      }
      $this->out(implode("\n".$this->indent, $rows));
      $this->table = array();
      $this->setLineBreaks(2);
    }
  }
  /**
   * properly pad content so it is aligned as whished
   * should be used with array_walk_recursive on $this->table['rows']
   *
   * @param string &$content
   * @param int $col
   * @return void
   */
  function alignTdContent(&$content, $col) {
    switch ($this->table['aligns'][$col]) {
      default:
      case 'left':
        $content .= str_repeat(' ', $this->table['col_widths'][$col] - $this->strlen($content));
        break;
      case 'right':
        $content = str_repeat(' ', $this->table['col_widths'][$col] - $this->strlen($content)).$content;
        break;
      case 'center':
        $paddingNeeded = $this->table['col_widths'][$col] - $this->strlen($content);
        $left = floor($paddingNeeded / 2);
        $right = $paddingNeeded - $left;
        $content = str_repeat(' ', $left).$content.str_repeat(' ', $right);
        break;
    }
  }
  /**
   * handle |  tags
   *
   * @param void
   * @return void
   */
  function handleTag_tr() {
    if ($this->parser->isStartTag) {
      $this->col = -1;
    } else {
      $this->row++;
    }
  }
  /**
   * handle | tags
   *
   * @param void
   * @return void
   */
  function handleTag_td() {
    if ($this->parser->isStartTag) {
      $this->col++;
      if (!isset($this->table['col_widths'][$this->col])) {
        $this->table['col_widths'][$this->col] = 0;
      }
      $this->buffer();
    } else {
      $buffer = trim($this->unbuffer());
      $this->table['col_widths'][$this->col] = max($this->table['col_widths'][$this->col], $this->strlen($buffer));
      $this->table['rows'][$this->row][$this->col] = $buffer;
    }
  }
  /**
   * handle | tags
   *
   * @param void
   * @return void
   */
  function handleTag_th() {
    if (!$this->keepHTML && !isset($this->table['rows'][1]) && !isset($this->table['aligns'][$this->col+1])) {
      if (isset($this->parser->tagAttributes['align'])) {
        $this->table['aligns'][$this->col+1] = $this->parser->tagAttributes['align'];
      } else {
        $this->table['aligns'][$this->col+1] = '';
      }
    }
    $this->handleTag_td();
  }
  /**
   * handle  tags
   *
   * @param void
   * @return void
   */
  function handleTag_dl() {
    if (!$this->parser->isStartTag) {
      $this->setLineBreaks(2);
    }
  }
  /**
   * handle  tags
   *
   * @param void
   * @return void
   **/
  function handleTag_dt() {
    if (!$this->parser->isStartTag) {
      $this->setLineBreaks(1);
    }
  }
  /**
   * handle  tags
   *
   * @param void
   * @return void
   */
  function handleTag_dd() {
    if ($this->parser->isStartTag) {
      if (substr(ltrim($this->parser->html), 0, 3) == '') {
        # next comes a paragraph, so we'll need an extra line
        $this->out("\n".$this->indent);
      } elseif (substr($this->output, -2) == "\n\n") {
        $this->output = substr($this->output, 0, -1);
      }
      $this->out(':   ');
      $this->indent('    ', false);
    } else {
      # lookahead for next dt
      if (substr(ltrim($this->parser->html), 0, 4) == '') {
        $this->setLineBreaks(2);
      } else {
        $this->setLineBreaks(1);
      }
      $this->indent('    ');
    }
  }
  /**
   * handle  tags (custom footnote references, see markdownify_extra::parseString())
   *
   * @param void
   * @return void
   */
  function handleTag_fnref() {
    $this->out('[^'.$this->parser->tagAttributes['target'].']');
  }
  /**
   * handle  tags (custom footnotes, see markdownify_extra::parseString()
   * and markdownify_extra::_makeFootnotes())
   *
   * @param void
   * @return void
   */
  function handleTag_fn() {
    if ($this->parser->isStartTag) {
      $this->out('[^'.$this->parser->tagAttributes['name'].']:');
      $this->setLineBreaks(1);
    } else {
      $this->setLineBreaks(2);
    }
    $this->indent('    ');
  }
  /**
   * handle  tag (custom footnotes, see markdownify_extra::parseString()
   *  and markdownify_extra::_makeFootnotes())
   *
   *  @param void
   *  @return void
   */
  function handleTag_footnotes() {
    if (!$this->parser->isStartTag) {
      $this->setLineBreaks(2);
    }
  }
  /**
   * parse a HTML string, clean up footnotes prior
   *
   * @param string $HTML input
   * @return string Markdown formatted output
   */
  function parseString($html) {
    /** TODO: custom markdown-extra options, e.g. titles & classes **/
    # ...
    # => 
    $html = preg_replace('@\s*\s*\d+\s*\s*@Us', '', $html);
    # 
    # =>
    # 
    #   ...
    #   ...
    # 
    $html = preg_replace_callback('##Us', array(&$this, '_makeFootnotes'), $html);
    return parent::parseString($html);
  }
  /**
   * replace HTML representation of footnotes with something more easily parsable
   *
   * @note this is a callback to be used in parseString()
   *
   * @param array $matches
   * @return string
   */
  function _makeFootnotes($matches) {
    # 
    #   ...
    #   ↩
    # # => ...
    # remove footnote link
    $fns = preg_replace('@\s*( \s*)?]*>↩\s*@s', '', $matches[1]);
    # remove empty paragraph
    $fns = preg_replace('@\s*@s', '', $fns);
    # ...-> ...
    $fns = str_replace(''.$fns.'';
    return preg_replace('#\s*(?=(?:))#s', '$1', $fns);
  }
} |