DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:lists:with_divs

This is just the orignal code for comparison's sake

<?php
if (! class_exists('syntax_plugin_lists')) {
  if (! defined('DOKU_PLUGIN')) {
    if (! defined('DOKU_INC')) {
      define('DOKU_INC', realpath(dirname(__FILE__).'/../../').'/');
    } // if
    define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
  } // if
  // include parent class
  require_once(DOKU_PLUGIN . 'syntax.php');
  define('PLUGIN_LISTS', 'plugin_lists');
 
/**
 * <tt>syntax_plugin_lists.php </tt>- A PHP4 class that implements
 * a <tt>DokuWiki</tt> plugin for <tt>un/ordered lists</tt> block
 * elements.
 *
 * <pre>
 *  Copyright (C) 2005 DFG/M.Watermann, D-10247 Berlin, FRG
 *      All rights reserved
 *    EMail : &lt;matthias@OLN.ComLink.APC.org&gt;
 * </pre>
 * <div class="disclaimer">
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.<br>
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * </div>
 * @author <a href="mailto:matthias@OLN.ComLink.APC.org">Matthias
 *  Watermann</a>
 * @since created 29-Aug-2005
 */
class syntax_plugin_lists extends DokuWiki_Syntax_Plugin {
// All DokuWiki plugins to extend the parser/rendering mechanism
// need to inherit from the DokuWiki_Syntax_Plugin class.
 
  /**
   * @publicsection
   */
  //@{
 
  /**
   * Tell the parser whether the plugin accepts syntax mode
   * <tt>$aMode</tt> within its own markup.
   *
   * @param $aMode String The requested syntaxmode.
   * @return Boolean <tt>TRUE</tt> unless <tt>$aMode</tt> is
   * <tt>PLUGIN_LISTS</tt> (which would result in a
   * <tt>FALSE</tt> method result).
   * @see getAllowedTypes()
   * @public
   */
  function accepts($aMode) {
    return (PLUGIN_LISTS != $aMode);
  } // accepts()
 
  /**
   * Connect lookup pattern to lexer.
   *
   * @param $aMode String The desired rendermode.
   * @see render($aFormat, $aRenderer, $aData)
   * @public
   */
  function connectTo($aMode) {
    if (PLUGIN_LISTS == $aMode) {
      return;
    } // if
    $this->Lexer->addEntryPattern('\n\x20{2,}[\x2A\x2D]\s*(?=(?s).*?[^\x5C]\x3C\n\n)',
      $aMode, PLUGIN_LISTS);
    $this->Lexer->addPattern('\n\x20{2,}[\x2A\x2D]\s*(?=(?s).*?[^\x5C]\x3C\n)',
      PLUGIN_LISTS);
    $this->Lexer->addEntryPattern('\n\t+\s*[\x2A\x2D]\s*(?=(?s).*?[^\x5C]\x3C\n\n)',
      $aMode, PLUGIN_LISTS);
    $this->Lexer->addPattern('\n\t+\s*[\x2A\x2D]\s*(?=(?s).*?[^\x5C]\x3C\n)',
      PLUGIN_LISTS);
  } // connectTo()
 
  /**
   * Get an associative array with plugin info.
   *
   * <p>
   * The returned array holds the following fields:
   * <dl>
   * <dt>author</dt><dd>Author of the plugin</dd>
   * <dt>email</dt><dd>Email address to contact the author</dd>
   * <dt>date</dt><dd>Last modified date of the plugin in
   * <tt>YYYY-MM-DD</tt> format</dd>
   * <dt>name</dt><dd>Name of the plugin</dd>
   * <dt>desc</dt><dd>Short description of the plugin (Text only)</dd>
   * <dt>url</dt><dd>Website with more information on the plugin
   * (eg. syntax description)</dd>
   * </dl>
   * @return Array Information about this plugin class.
   * @static
   * @public
   */
  function getInfo() {
    return array(
      'author' => 'Matthias Watermann',
      'email' => 'support@mwat.de',
      'date' => '2005-09-03',
      'name' => 'List Syntax Plugin',
      'desc' => 'Add HTML Style Un/Ordered Lists',
      'url' =>  'http://www.dokuwiki.org/plugin:lists');
  } // getInfo()
 
  /**
   * Define how this plugin is handled regarding paragraphs.
   *
   * <p>
   * This method is important for correct XHTML nesting. It returns
   * one of the following values:
   * </p>
   * <dl>
   * <dt>normal</dt><dd>The plugin can be used inside paragraphs.</dd>
   * <dt>block</dt><dd>Open paragraphs need to be closed before
   * plugin output.</dd>
   * <dt>stack</dt><dd>Special case: Plugin wraps other paragraphs.</dd>
   * </dl>
   * @return String <tt>'normal'</tt> .
   * @static
   * @public
   */
  function getPType() {
    return 'normal';
  } // getPType()
 
  /**
   * Where to sort in?
   *
   * @return Integer <tt>8</tt>, an arbitrary value smaller
   * <tt>Doku_Parser_Mode_listblock</tt> (10).
   * @static
   * @public
   */
  function getSort() {
    // class 'Doku_Parser_Mode_preformated' returns 20
    // class 'Doku_Parser_Mode_listblock' returns 10
    return 8;
  } // getSort()
 
  /**
   * Get the type of syntax this plugin defines.
   *
   * @return String <tt>'container'</tt>.
   * @static
   * @public
   */
  function getType() {
    return 'container';
  } // getType()
 
  /**
   * Handler to prepare matched data for the rendering process.
   *
   * <p>
   * The <tt>$aState</strong> parameter gives the type of pattern
   * which triggered the call to this method:
   * </p>
   * <dl>
   * <dt>DOKU_LEXER_ENTER</dt>
   * <dd>a pattern set by <tt>addEntryPattern()</tt></dd>
   * <dt>DOKU_LEXER_MATCHED</dt>
   * <dd>a pattern set by <tt>addPattern()</tt></dd>
   * <dt>DOKU_LEXER_EXIT</dt>
   * <dd> a pattern set by <tt>addExitPattern()</tt></dd>
   * <dt>DOKU_LEXER_SPECIAL</dt>
   * <dd>a pattern set by <tt>addSpecialPattern()</tt></dd>
   * <dt>DOKU_LEXER_UNMATCHED</dt>
   * <dd>ordinary text encountered within the plugin's syntax mode
   * which doesn't match any pattern.</dd>
   * </dl>
   * @param $aMatch String The text matched by the patterns.
   * @param $aState Integer The lexer state for the match.
   * @param $aPos Integer The character position of the matched text.
   * @param $aHandler Object Reference to the Doku_Handler object.
   * @return Array Index <tt>[0]</tt> holds the current
   * <tt>$aState</tt>, index <tt>[1]</tt> the match prepared for
   * the <tt>render()</tt> method.
   * @see render($aMode, $aRenderer, $aData)
   * @static
   * @public
   */
  function handle($aMatch, $aState, $aPos, &$aHandler) {
    switch ($aState) {
      case DOKU_LEXER_ENTER:
        // fall through
      case DOKU_LEXER_MATCHED:  // DTs
        $hits = array();
        if (preg_match('|\n*((\s*)(.))|', $aMatch, $hits)) {
          return array($aState, $hits[3],
            strlen(str_replace('  ', "\t", $hits[2])));
        } // if
        return array($aState, $aMatch);
      case DOKU_LEXER_UNMATCHED:  // LIs
        $hits = array();
        if (preg_match('|^\s*\x3C$|', $aMatch, $hits)) {
          return array($aState, '', +1);
        } // if
        if (preg_match('|(.*?)\s+\x3C$|s', $aMatch, $hits)) {
          return array($aState,
            str_replace('\<', '<', $hits[1]), +1);
        } // if
        if (preg_match('|(.*[^\x5C])\x3C$|s', $aMatch, $hits)) {
          return array($aState,
            str_replace('\<', '<', $hits[1]), +1);
        } // if
        return array($aState, str_replace('\<', '<', $aMatch), -1);
      case DOKU_LEXER_EXIT:
        // end of list
      default:
        return array($aState);
    } // switch
  } // handle()
 
  /**
   * Add exit pattern to lexer.
   *
   * @public
   */
  function postConnect() {
    $this->Lexer->addExitPattern('(?<=\x3C)\n(?=\n)', PLUGIN_LISTS);
  } // postConnect()
 
  /**
   * Handle the actual output creation.
   *
   * <p>
   * The method checks for the given <tt>$aFormat</tt> and returns
   * <tt>FALSE</tt> when a format isn't supported. <tt>$aRenderer</tt>
   * contains a reference to the renderer object which is currently
   * handling the rendering. The contents of <tt>$aData</tt> is the
   * return value of the <tt>handle()</tt> method.
   * </p>
   * @param $aFormat String The output format to generate.
   * @param $aRenderer Object A reference to the renderer object.
   * @param $aData Array The data created by the <tt>handle()</tt>
   * method.
   * @return Boolean <tt>TRUE</tt> if rendered successfully, or
   * <tt>FALSE</tt> otherwise.
   * @see handle($aMatch, $aState, $aPos, $aHandler)
   * @static
   * @public
   */
  function render($aFormat, &$aRenderer, &$aData) {
    if ('xhtml' != $aFormat) {
      return FALSE;
    } // if
    static $CHARS; static $ENTS;
    if (! is_array($CHARS)) {
      $CHARS = array('&','<', '>');
    } // if
    if (! is_array($ENTS)) {
      $ENTS = array('&#38;', '&#60;', '&#62;');
    } // if
    static $LISTS = array('*' => 'ul', '-' => 'ol');
    static $LEVEL = 1;
    static $INLI = array();  // INLI[LEVEL] :: 0==open LI, 1==open LI/P
    static $CURRENT = array();  // CURRENT[LEVEL] :: * | -
    switch ($aData[0]) {
      case DOKU_LEXER_ENTER:
        $CURRENT[$LEVEL] = $aData[1];
        $hits = array();
        if (preg_match('|\s*<p>\s*$|i', $aRenderer->doc, $hits)) {
          $hits = -strlen($hits[0]);
          $aRenderer->doc = substr($aRenderer->doc, 0, $hits)
            . "\n<{$LISTS[$aData[1]]}>";
        } else {
          $aRenderer->doc .= "</p><{$LISTS[$aData[1]]}>";
        } // if
        // fall through to handle first item
      case DOKU_LEXER_MATCHED:
        // $aData[0] :: match state
        // $aData[1] :: * | -
        // $aData[2] :: nesting level
        $diff = $aData[2] - $LEVEL;
        if (0 < $diff) {  // going up one level
          $CURRENT[++$LEVEL] = $aData[1];
          $hits = array();
          if (preg_match('|</li>\s*$|', $aRenderer->doc)) {
            // need to open a new LI 
            $aRenderer->doc .= "\n" . str_repeat("\t", $LEVEL)
              . '<li class="level' . ($LEVEL - 1) . '"><'
              . $LISTS[$CURRENT[$LEVEL]] . '>';
            $INLI[$LEVEL - 1] = 0; // no closing P needed
          } else if (preg_match('|\s*<li[^>]*>\s*<p>\s*$|',
            $aRenderer->doc, $hits)) {
            // replace rudimentary LI
            $hits = -strlen($hits[0]);
            $aRenderer->doc = substr($aRenderer->doc, 0, $hits)
              . "\n" . str_repeat("\t", $LEVEL)
              . '<li class="level' . ($LEVEL - 1)
              . '"><' . $LISTS[$CURRENT[$LEVEL]] . ">\n";
            $INLI[$LEVEL - 1] = 0; // no closing P needed
          } else {  // possibly open LI
            if (isset($INLI[$LEVEL - 1])) {
              if (0 < $INLI[$LEVEL - 1]) {  // open LI P
                $aRenderer->doc .= '</p><'
                  . $LISTS[$aData[1]] . '>';
                $INLI[$LEVEL - 1] = 0;
              } else {  // open LI
                $aRenderer->doc .= "\n"
                  . str_repeat("\t", $LEVEL) . '<'
                  . $LISTS[$aData[1]]. '>';
              } // if
            } else {  // no open LI
              $aRenderer->doc .= "\n"
                . str_repeat("\t", $LEVEL)
                . '<li class="level' . ($LEVEL - 1) . '"><'
                . $LISTS[$aData[1]] . '>';
              $INLI[$LEVEL - 1] = 0; // no closing P needed
            } // if
          } // if
        } else if (0 > $diff) {  // going back some levels
          do {
            --$LEVEL;
            $aRenderer->doc .= "\n". str_repeat("\t", $LEVEL)
              . '</' . $LISTS[$CURRENT[$LEVEL + 1]]. '>';
            if (isset($INLI[$LEVEL])) {
              if (0 < $INLI[$LEVEL]) {
                $aRenderer->doc .= '</p></li>';
              } else {
                $aRenderer->doc .= '</li>';
              } // if
            } // if
          } while (0 > ++$diff);
        } else if ($aData[1] !=  $CURRENT[$LEVEL]) {
          // list type changed
          if (isset($INLI[$LEVEL])) {
            if (0 < $INLI[$LEVEL]) {
              $aRenderer->doc .= '</p></li>';
            } else {
              $aRenderer->doc .= '</li>';
            } // if
          } // if
          $aRenderer->doc .= "\n</"
            . $LISTS[$CURRENT[$LEVEL]] . ">\n"
            . str_repeat("\t", $LEVEL - 1) . '<' 
            . $LISTS[$aData[1]] . '>';
          $CURRENT[$LEVEL] = $aData[1];
        } // if
        $aRenderer->doc .= "\n" . str_repeat("\t", $LEVEL)
          . '<li class="level' . $LEVEL . '"><p>';
        $INLI[$LEVEL] = 1;  // closing P needed
        return TRUE;
      case DOKU_LEXER_UNMATCHED:
        // $aData[0] :: match state
        // $aData[1] :: text
        // $aData[2] :: +1(EoT), -1(start/inbetween)
        if (0 < $aData[2]) {
          // last part of item's text
          if (strlen($aData[1])) {
            if (isset($INLI[$LEVEL])) {
              if (0 < $INLI[$LEVEL]) {  // LI P
                $aRenderer->doc .=
                  str_replace($CHARS, $ENTS, $aData[1])
                  . '</p></li>';
              } else {  // LI
                $aRenderer->doc .= '<p>'
                  . str_replace($CHARS, $ENTS, $aData[1])
                  . '</p></li>';
              } // if
            } else {  // no LI
              if (1 < $LEVEL) {  // assume a trailing LI text
                --$LEVEL;
                $aRenderer->doc .= "\n"
                  . str_repeat("\t", $LEVEL) . '</' 
                  . $LISTS[$CURRENT[$LEVEL + 1]] . '><p>'
                  . str_replace($CHARS, $ENTS, $aData[1])
                  . '</p></li>';
              } else {
//XXX: There must be no data w/o context; the markup is broken. Whatever we
// could do it would be WRONG (and break XHMTL validity); hence comment:
                $aRenderer->doc .= '<!-- '
                  . str_replace($CHARS, $ENTS, $aData[1])
                  . ' -->';
              } // if
            } // if
          } else {  // empty data
            $hits = array();
            if (preg_match('|\s*<li[^>]*>\s*<p>\s*$|',
              $aRenderer->doc, $hits)) {
              $hits = -strlen($hits[0]);
              // remove empty list item
              $aRenderer->doc =
                substr($aRenderer->doc, 0, $hits);
            } else if (preg_match('|\s*<p>\s*$|',
              $aRenderer->doc, $hits)) {
              $hits = -strlen($hits[0]);
              $aRenderer->doc =
                substr($aRenderer->doc, 0, $hits) . '</li>';
            } else if (isset($INLI[$LEVEL])) {
              if (0 < $INLI[$LEVEL]) {
                $aRenderer->doc .= '</p></li>';
              } else {
                $aRenderer->doc .= '</li>';
              } // if
            } // if
          } // if
          unset($INLI[$LEVEL]);
        } else {
          // item part between substitutions or nested blocks
          if (isset($INLI[$LEVEL])) {
            if (0 < $INLI[$LEVEL]) {  // LI P
              $aRenderer->doc .=
                str_replace($CHARS, $ENTS, $aData[1]);
              $INLI[$LEVEL] = 1;
            } else {  // LI
              $aRenderer->doc .= '<p>'
                . str_replace($CHARS, $ENTS, $aData[1]);
            } // if
          } else {  // data w/o context
            if (1 < $LEVEL) {  // assume a trailing LI text
              --$LEVEL;
              $aRenderer->doc .= "\n"
                . str_repeat("\t", $LEVEL) . '</' 
                . $LISTS[$CURRENT[$LEVEL + 1]] . '><p>'
                . str_replace($CHARS, $ENTS, $aData[1]);
              $INLI[$LEVEL] = 1;
            } else {
              $aRenderer->doc .=
                str_replace($CHARS, $ENTS, $aData[1]);
            } // if
          } // if
        } // if
        return TRUE;
      case DOKU_LEXER_EXIT:
        while (1 < $LEVEL) {
          --$LEVEL;
          $aRenderer->doc .= "\n" . str_repeat("\t", $LEVEL)
            . '</' . $LISTS[$CURRENT[$LEVEL + 1]]. '>';
          if (isset($INLI[$LEVEL])) {
            if (0 < $INLI[$LEVEL]) {
              $aRenderer->doc .= '</p></li>';
            } else {
              $aRenderer->doc .= '</li>';
            } // if
          } // if
        } // while
        $aRenderer->doc = preg_replace('|<p>\s*</p>\s*|', '',
          $aRenderer->doc) . "\n</{$LISTS[$CURRENT[$LEVEL]]}>\n<p>";
        $CURRENT = $INLI = array();
        $LEVEL = 1;
      default:
        return TRUE;
    } // switch
  } // render()
 
  //@}
} // class syntax_plugin_lists
} // if
?>

Matthias Watermann 2005-09-03 This modification created without his permission I'm really sorry if he disapproves, but I think a lot of people will want the compatibility with dokuwiki.

plugin/lists/with_divs.txt · Last modified: 2005-10-05 09:55 (external edit)