DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:lists

Lists Syntax PlugIn

Compatible with DokuWiki

2005-07-13, !Lemming

plugin (X)HTML style un/ordered lists

Last updated on
2008-03-28
Provides
Syntax

This extension has not been updated in over 2 years. It may no longer be maintained or supported and may have compatibility issues.

Similar to mllist, yalist

Tagged with formatting, list

While DokuWiki's current implementation of un/ordered lists satisfies the need for basic list markup, it does not allow for embedding several block elements1) in a list item. Depending on the structure of a certain document this may be just enough. Sometimes, however, there is actually a need for nesting other block elements in a list item. And that's where this plugin enters the game: It provides the ability to wrap other block elements2) in a list item.

:!: Note: This plugin functionally replaces (and enhances) DokuWiki's built-in handling of un/ordered lists. Both list markup styles must not be mixed in a document3). If you won't ever need un/ordered lists with nested block ele­ments do not install this plugin.

Usage

The markup implemented by this plugin is very similar to DokuWiki's built-in syntax:

	* first unordered list item <
	* second unordered list item <
	* third unordered list item <

	- first ordered list item <
	- second ordered list item <
	- third ordered list item <

Note the <4) character marking the end of each list item5); optional white­space6) before the less_than character is silently ignored. The end of a list is marked up by an additional newline; in other words: The very last item of a list (i.e. its terminating less_than character) is followed by two consecutive linefeeds7).

Each list item's first line8) must be indented by at least two spaces9) or a TAB10) (one TAB is worth two spaces et vice versa) followed by either a * (asterisk11)) for items of an unordered list or a - (hyphen12)) for items of an ordered list. The text between the list type indicator (asterisk or hyphen) and the less_than character is used as the list item's contents.

The contents of a list item can contain anything including other lists (of the same or a different kind), several paragraphs, tables, code blocks etc. (see the examples below). When using linefeeds in list items the second and all following lines must not be indented because that would cause DokuWiki's built-in preformatted/code parser/renderer to markup those lines as preformatted code blocks — but, of course, that can be exactly the intention of the indentation…

Examples

The simplest form of un/ordered list's markup is already shown above. The resulting HTML would look like this:

<ul>
  <li class="level1"><p>first unordered list item</p></li>
  <li class="level1"><p>second unordered list item</p></li>
  <li class="level1"><p>third unordered list item</p></li>
</ul>
<ol>
  <li class="level1"><p>first ordered list item</p></li>
  <li class="level1"><p>second ordered list item</p></li>
  <li class="level1"><p>third ordered list item</p></li>
</ol>

Item with several paragraphs

Supposing we need more than just one (possibly huge) paragraph in an item's contents the wiki markup could look like this:

  * first one-liner item <
  * first paragraph of second item.

Second paragraph
of second item.

Third paragraph
of second item. <
  * A two-liner\\ list item <

As usual an empty line marks the start of a new paragraph. The resulting HTML would be as follows:

<ul>
  <li class="level1"><p>first one-liner item</p></li>
  <li class="level1"><p>first paragraph of second item.</p>
    <p>Second paragraph  of second item.</p>
    <p>Third paragraph  of second item.</p></li>
  <li class="level1"><p>A two-liner<br/>
 list item</p></li>
</ul>

The empty lines in the HTML output are not caused by this plugin but by DokuWiki's built-in paragraph handling.

Mixing list types

The plugin allows for easily changing the list type (un/ordered) within a list. Consider the following example:

  * item one <
  - item two <
  * item three <
  - item four <

Can you imagine the resulting HTML code? Well, here it comes:

<ul>
  <li class="level1"><p>item one</p></li>
</ul>
<ol>
  <li class="level1"><p>item two</p></li>
</ol>
<ul>
  <li class="level1"><p>item three</p></li>
</ul>
<ol>
  <li class="level1"><p>item four</p></li>
</ol>

Surprised? — No? — OK. — While this example doesn't really make sense, it at least demonstrates the ease of list handling provided by this plugin. The automatic list type handling actually comes in handy with the next example.

Nesting lists

What about a list of lists? That's easily marked up by indenting the nested list items by two more spaces13). Here, for instance, we'll get a three levels deep list:

  * item one <
  * item two
    - no.1 of item two<
    - no.2 of item two
      * first of item two's no.2<
      * second of item two's no.2<
<
<

Note the two less_than characters at the end: the first one terminates the no.2 of item two (which contains a nested unordered list) and the second closes the item two (containing an ordered list with an embedded unordered list). — There can be, of course, additional text before those lonely less_than characters (which would end up as trailing paragraphs). Please note, however, that such text must not start with markup14); in case you need markup here, just place a space15) at the line's start to make it work correctly16).

The resulting HTML markup:

<ul>
  <li class="level1"><p>item one</p></li>
  <li class="level1"><p>item two</p><ol>
    <li class="level2"><p>no.1 of item two</p></li>
    <li class="level2"><p>no.2 of item two</p><ul>
      <li class="level3"><p>first of item two's no.2</p></li>
      <li class="level3"><p>second of item two's no.2</p></li>
    </ul></li>
  </ol></li>
</ul>

Now consider the almost identical markup:

  * item one <
  * item two
    - no.1 of item two<
    - no.2 of item two<
      * first of ?<
      * second of ?<
<

Here no.2 of item two is closed before the first of ? item is added. In consequence the following unordered list cannot be a “child” of the latter but gets embedded in a new (automatically created) list item which contains nothing else apart from the “child list”. It results in this HTML code:

<ul>
  <li class="level1"><p>item one</p></li>
  <li class="level1"><p>item two</p><ol>
    <li class="level2"><p>no.1 of item two</p></li>
    <li class="level2"><p>no.2 of item two</p></li>
      <li class="level2"><ul>
      <li class="level3"><p>first of ?</p></li>
      <li class="level3"><p>second of ?</p></li>
    </ul></li>
  </ol></li>
</ul>

A third (slightly different) variant would be this:

  * item one <
  * item two <
    - no.1 of item two<
    - no.2 of item two<
      * first of ?<
      * second of ?<

Here each list item is closed before another one is opened. This means that both nested lists are embedded in, say, “anonymous” list items. The HTML code here:

<ul>
  <li class="level1"><p>item one</p></li>
  <li class="level1"><p>item two</p><ol>
    <li class="level2"><p>no.1 of item two</p></li>
    <li class="level2"><p>no.2 of item two</p></li>
      <li class="level2"><ul>
      <li class="level3"><p>first of ?</p></li>
      <li class="level3"><p>second of ?</p></li>
    </ul></li>
  </ol></li>
</ul>

Installation

Search and install the plugin using the Extension Manager. Refer to Plugins on how to install plugins manually.

Plugin Source

Here comes the GPLed PHP source17) for those who'd like to scan it be­fore actu­ally in­stal­ling it:

<?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.
 *
 * <p>
 * Usage:<br>
 * <tt>  * unordered item &lt;</tt>
 * <tt>  - ordered item &lt;</tt>
 * </p>
 * <pre>
 *  Copyright (C) 2005, 2007  DFG/M.Watermann, D-10247 Berlin, FRG
 *      All rights reserved
 *    EMail : &lt;support@mwat.de&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
 * <a href="http://www.gnu.org/licenses/gpl.html">version 3</a> 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:support@mwat.de">Matthias Watermann</a>
 * @version <tt>$Id: syntax_plugin_lists.php,v 1.4 2007/08/15 12:36:19 matthias Exp $</tt>
 * @since created 29-Aug-2005
 */
class syntax_plugin_lists extends DokuWiki_Syntax_Plugin {
 
  /**
   * @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).
   * @public
   * @see getAllowedTypes()
   */
  function accepts($aMode) {
    return (PLUGIN_LISTS != $aMode);
  } // accepts()
 
  /**
   * Connect lookup pattern to lexer.
   *
   * @param $aMode String The desired rendermode.
   * @public
   * @see render()
   */
  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.
   * @public
   * @static
   */
  function getInfo() {
    return array(
      'author' =>  'Matthias Watermann',
      'email' =>  'support@mwat.de',
      'date' =>  '2007-08-15',
      'name' =>  'List Syntax Plugin',
      'desc' =>  'Add HTML Style Un/Ordered Lists',
      'url' =>  'https://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> .
   * @public
   * @static
   */
  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).
   * @public
   * @static
   */
  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>.
   * @public
   * @static
   */
  function getType() {
    return 'container';
  } // getType()
 
  /**
   * Handler to prepare matched data for the rendering process.
   *
   * <p>
   * The <tt>$aState</tt> 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.
   * @public
   * @see render()
   * @static
   */
  function handle($aMatch, $aState, $aPos, &$aHandler) {
    static $CHARS; static $ENTS;
    if (! is_array($CHARS)) {
      $CHARS = array('&','<', '>');
    } // if
    if (! is_array($ENTS)) {
      $ENTS = array('&#38;', '&#60;', '&#62;');
    } // if
    switch ($aState) {
      case DOKU_LEXER_ENTER:
        // fall through
      case DOKU_LEXER_MATCHED:
        $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:
        $hits = array();
        if (preg_match('|^\s*\x3C$|', $aMatch, $hits)) {
          return array(DOKU_LEXER_UNMATCHED, '', +1);
        } // if
        if (preg_match('|(.*?)\s+\x3C$|s', $aMatch, $hits)) {
          return array(DOKU_LEXER_UNMATCHED,
            str_replace($CHARS, $ENTS,
              str_replace('\<', '<', $hits[1])), +1);
        } // if
        if (preg_match('|(.*[^\x5C])\x3C$|s', $aMatch, $hits)) {
          return array(DOKU_LEXER_UNMATCHED,
            str_replace($CHARS, $ENTS,
              str_replace('\<', '<', $hits[1])), +1);
        } // if
        return array(DOKU_LEXER_UNMATCHED,
          str_replace($CHARS, $ENTS,
            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() {
    // make sure the RegEx 'eats' only _one_ LF:
    $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.
   * @public
   * @see handle()
   */
  function render($aFormat, &$aRenderer, &$aData) {
    if ('xhtml' != $aFormat) {
      return FALSE;
    } // if
    static $LISTS = array('*' => 'ul', '-' => 'ol');
    static $LEVEL = 1;  // initial nesting level
    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)
            . '<' . $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 .= '<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)
              . '<li class="level' . ($LEVEL - 1)
              . '"><' . $LISTS[$CURRENT[$LEVEL]] . '>';
            $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 .= '<'
                  . $LISTS[$aData[1]] . '>';
              } // if
            } else {  // no open LI
              $aRenderer->doc .= '<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 .= '</'
              . $LISTS[$CURRENT[$LEVEL + 1]] . '>';
            if (isset($INLI[$LEVEL])) {
              $aRenderer->doc .= (0 < $INLI[$LEVEL])
                ? '</p></li>'
                : '</li>';
            } // if
          } while (0 > ++$diff);
        } else if ($aData[1] !=  $CURRENT[$LEVEL]) {
          // list type changed
          if (isset($INLI[$LEVEL])) {
            $aRenderer->doc .= (0 < $INLI[$LEVEL])
              ? '</p></li>'
              : '</li>';
          } // if
          $aRenderer->doc .= '</' . $LISTS[$CURRENT[$LEVEL]]
            . '><' . $LISTS[$aData[1]] . '>';
          $CURRENT[$LEVEL] = $aData[1];
        } // if
        $aRenderer->doc .= '<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])) {
              $aRenderer->doc .= (0 < $INLI[$LEVEL]) // LI P
                ? $aData[1] . '</p></li>'
                : '<p>' . $aData[1] . '</p></li>';
            } else {  // no LI
              if (1 < $LEVEL) {  // assume a trailing LI text
                --$LEVEL;
                $aRenderer->doc .= '</'
                  . $LISTS[$CURRENT[$LEVEL + 1]] . '><p>'
                  . $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 .= '<!-- '. $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])) {
              $aRenderer->doc .= (0 < $INLI[$LEVEL])
                ? '</p></li>'
                : '</li>';
            } // 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 .= $aData[1];
              $INLI[$LEVEL] = 1;
            } else {  // LI
              $aRenderer->doc .= '<p>' . $aData[1];
            } // if
          } else {  // data w/o context
            if (1 < $LEVEL) {  // assume a trailing LI text
              --$LEVEL;
              $aRenderer->doc .= '</'
                . $LISTS[$CURRENT[$LEVEL + 1]] . '><p>'
                . $aData[1];
              $INLI[$LEVEL] = 1;
            } else {
              $aRenderer->doc .= $aData[1];
            } // if
          } // if
        } // if
        return TRUE;
      case DOKU_LEXER_EXIT:
        while (1 < $LEVEL) {
          --$LEVEL;
          $aRenderer->doc .= '</'. $LISTS[$CURRENT[$LEVEL + 1]] .'>';
          if (isset($INLI[$LEVEL])) {
            $aRenderer->doc .= (0 < $INLI[$LEVEL])
              ? '</p></li>'
              : '</li>';
          } // if
        } // while
        // Since we have to use PType 'normal' we must open
        // a new paragraph for the following text
        $aRenderer->doc = preg_replace('|\s*<p>\s*</p>\s*|', '',
          $aRenderer->doc) . '</'. $LISTS[$CURRENT[$LEVEL]] .'><p>';
        $CURRENT = $INLI = array();
        $LEVEL = 1;
      default:
        return TRUE;
    } // switch
  } // render()
 
  //@}
} // class syntax_plugin_lists
} // if
//Setup VIM: ex: et ts=2 enc=utf-8 :
?>

Presentation

The accompanying CSS presentation rules:

ol,ul,div.dokuwiki ol,div.dokuwiki ul{list-style-position:outside;list-style-image:none;margin:0 0 0.6ex 0;padding:0 0 0 1ex;background:#fff;color:#000;text-align:left;line-height:1.4;}
ol li,ul li{padding:0;}
ol li{margin:0 0 0 3.5ex;}
ul li{margin:0 0 0 2ex;}
ol li div,ul li div,ol li p,ul li p{margin:0;padding:0;font-weight:normal;}
ul{list-style-type:disc;}
ul ul{list-style-type:square;}
ol{list-style-type:decimal;}
ol ol{list-style-type:upper-roman;}
ol ol ol{list-style-type:lower-alpha;}
ol ol ol ol{list-style-type:lower-greek;}
ol li div.li,ul li div.li{display:inline;}

Of course, you're free to modify this styles18) to suit your personal needs or aesthe­tics19).

Changes

2008-03-28:
* minor CSS changes;

2007-08-15:
* added GPL link and fixed some doc problems;

2007-01-05:
* moved entity replacements from 'render()' to 'handle()';
* modified 'render()' to avoid useless whitespace;

2005-09-03:
+ first public release

Matthias Watermann 2008-03-28

See also

Plugins by the same author

Discussion

Hints, comments, suggestions …

To be compatible with default css style, a <div class=“li”> is missing. You should generate something like this :

<ol>
  <li class="level1"><div class="li"><p>blahblahblah</p></div></li>
</ol>
Thanks for your note, but I consider that additional DIV pointless (and a waste of memory and bandwidth, by the way). It's not needed at all for CSS as one can always use li, ol li or ol li.level1 to select the item wanted. So I intentionally left that superfluous markup away.
Matthias Watermann 2005-09-13
Just a note. The div is there with an intention. This is the only way to style the number of a list item different than the item itself. The li gets a blue colour and for the div it is set back to black again. — Andreas Gohr 2005-09-13 19:22
While that looks like an interesting effect I don't think that it's worth the DIV since it blows up the document's structure w/o any real benefit. A CSS compliant way for colouring your list numbers would be something like this:
ol li.level1 { color: #009; }
ol li.level2 { color: #090; }
ol li.level3 { color: #900; }
ol li > * { color: #000; }
You could even omit the child-selector > to support outdated browsers not supporting CSS2 (like M$IE) and still get the visual effect you want w/o any additional HTML markup.
Matthias Watermann 2005-09-14 07:40
Ok, as I use my own template, a CSS modification seems better than an additional HTML markup. Thanks. — Samuel Degrande 2005-09-14 11:26
I'm using IE6 and you definitely need the extra div tag in my version. I have hacked it to work but I thought I would raise the point - very useful plugin by the way. Dav Bacci 26th Jan 2006

It would be nice if the plugin provided a way to specify the starting point of ordered lists (e.g. start a list with 3.). I realize that the start attribute is deprecated, but I believe there is a way to do this in CSS.
Joe Nahmias 2006-07-18 11:40

The lists plugin is definitely useful, especially when working with multi-paragraph list items or those with a mixture of text and graphics. I'd like to second the request to fix the div tag - without it unordered list text displays as regular blue and ordered list text displays as bold blue.
Eric Sisler 2006-11-17 18:41

Colouring is a matter of CSS, not (X)HTML (see the discussion/example above). – Besides, are you sure that using different colours for the list item marker and its content does enhance your readers understanding? Personally I'd consider such fancy games just distracting – but, of course, it's a matter of habit.
Matthias Watermann 2007-01-18 13:05

Neat idea for a plugin, but I don't like it since it breaks existing wiki syntax. Have you thought about just having a special tag for list items which need to contain other items?

  * List item 1
  * List item 2
  *> list item 3 with nesting

  Nested paragraph.

  >
  * List item 4
I'm not sure what you mean with “breaks existing wiki syntax”.
(1.) As noted above the whole point of this plugin was to provide a wiki markup that resembles the whole power of (X)HTML lists. Compared with DokuWiki's basic list markup of course such an approach must use a markup that differs from the built-in one.
(2.) Even after installing this plugin you can still use the “old” markup style because the plugin's RegEx won't match that built-in list markup.
(3.) Although strongly deprecated21) under some conditions it's even possible to use both markup styles in a single page22).
Matthias Watermann 2007-07-16 12:35

Thank you for a nice plugin. I have a question and a remark:

  • Is there a way to reduce spacing between items to the same as in the basic lists? I changed “line-height:1.4” in plugin's style.css to “line-height:1.5em” (as in DokuWiki's default.css), but it didn't help – items are still too widely spaced.
  • I no expert at all in CSS or the <div> issue discussed above, but I am noticing that this plugin modifies default styling of some elements:
    • item text is colored the same as item number (as discussed above already)
    • <code> blocks use bold text now (inherit from item number, I guess)

> Try installing the Code Syntax Plugin: It comes with its own CSS file which you could modify in case the colouring doesn't match your taste.
Matthias Watermann 2008/03/28 12:49

To get these things back I (again not an expert in CSS) had to modify style.css (add color:text;) as well as DokuWiki's default.css (add font-weight: normal; line-height: 1.2; to div.dokuwiki pre). Modifying default.css just to get back the default behavior does not seem right, and I am not sure I took care of all possible cases (i.e. may be other elements are affected as well?). Is there another way?

The line-height setting of DokuWiki's default CSS is just wrong (i.e. too small): It doesn't take into account that (a) there are characters with ascenders/descenders and (b) the footnote links are placed above the standard character's topline. That results in a broken layout23). So instead of trying to reproduce the errors of DokuWiki's default CSS with all plugins one should correct or replace the default CSS. If you care for your readers you should never use a line-height setting smaller 1.3 (but possibly greater) and never use a unit qualifier (such as em, ex etc.).
Matthias Watermann 2008/03/28 12:33

I really like the idea of this plugin, especially that you give three nesting variants. But, the 2nd and 3rd variants are broken. In the 2nd variant, the “first of ?” has “3. •” in front of it. The 3rd variant's “no.1 of item two” has “• 1.” as well as what happens in the 2nd variant. In either case, if you try to put an additional 2nd level ol item, it becomes 4 instead of 3. — Saxywolf 2009/11/03

I don't mean to continue the css discussion, but shouldn't the style be left up to the DokuWiki Template chosen by the Admin (whether good or bad) and not be touched by a plugin modifying a basic structural component? — Saxywolf 2009/11/03

Example or clearification regarding continuing list items after a nested list

The section on Nesting lists is a bit confusing because it can be read like it is possible to continue a list item after specifying nested list items, but there's no example. If it is really possible, please add one, because < and <? don't explain themselves. — krichter 2014-12-18 20:32


1)
such as paragraphs, tables or other (nested) lists etc.
2)
inline markup, of course, is possible as usual
3)
otherwise unexpected results will occur which are likely to render the page's markup invalid
4)
less_than, ASCII char #60
5)
This additional less_than character makes the difference compared to DokuWiki's built-in syntax where the very first linefeed character marks the end of the list item.
6)
Whitespace: any of the space (ASCII #32), formfeed (ASCII #12), newline (ASCII #10), carriage return (ASCII #13), horizontal tab (ASCII #9) characters
7)
linefeed (newline), ASCII char #10
8)
In contrast to DokuWiki's built-in list markup this plugin does allow a list item to span several lines in a wiki document.
9)
space, ASCII char #32; not to be confused with blanks, ASCII char #255
10)
tabulator, ASCII char #9
11)
asterisk, ASCII char #42
12)
hyphen (minus sign), ASCII char #45
13)
or one TABulator character
14)
e.g. strong, emphasized or underlined or block elements like e.g. tables
15)
and, as with elements which need to begin on a newline (like tables) an additional linefeed character
16)
This is because without lookahead for the following markup to come (which is not possible with DokuWiki's current parser) there's no way to automatically figure out what such a text is supposed to “mean”, formally speaking, and unexpected/unintended paragraphs will be inserted, or inline markup ends up without a context.
17)
The comments within the source file are suitable for the OSS doxygen tool, a do­cu­men­ta­tion sy­stem for C++, C, Java, Ob­jec­tive-C, Python, IDL and to some extent PHP, C#, and D. — Since I'm working with dif­fe­rent pro­gram­ming lan­gua­ges it's a great ease to have one tool that handles the docs for all of them.
18)
The source archive contains a commented and indented stylesheet for your in­for­ma­tion.
19)
Just be careful when modifying a CSS file: both the order and the selector grou­pings are im­por­tant for CSS to work as intended/expected.
20)
obsoleted by incorporating its ability into the Code plugin
21)
after all, one should stick to one style of markup unless you like to bewilder your users
22)
if the “extended” list is placed before the “basic” one
23)
the visual effect of which is increased if your browser has to make the font-size bigger to make the pages legible since DokuWiki's default setting is far too small for modern high-resolution displays
plugin/lists.txt · Last modified: 2023-03-04 15:44 by Aleksandr

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki