DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:dl

Definition List Syntax Plugin

Compatible with DokuWiki

Frusterick Manners

plugin Another (X)HTML definition list. IMHO with better (simpler and smarter) syntax

Last updated on
2008-04-17
Provides
Syntax

The missing download url means that this extension cannot be installed via the Extension Manager. Please see Publishing a Plugin on dokuwiki.org. Recommended are public repository hosts like GitHub, GitLab or Bitbucket.

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 definition, definitionlist, definitions, deflist, dl, yalist

Tagged with definitions, formatting, list

There are at least two other plugins for definition lists with different syntax and different implementation. Choose which you prefer: definitionlist, deflist. The reason for a new implementation is the following: Simple and good looking syntax, high flexibility.

The definition list created with this plugin can be linked by the plugin for explanation of terms.

Flexibility

The definition list is as flexible, as the XHTML definition list (<dl><dt></dt><dd></dd></dl>). It offers the same possiblilities, including:

  • you can have multiple lines for a text
  • you can have multiple titles
  • you can have multiple descriptions
  • and you can have any other element nested inside the definition list
    • including lists
    • and the definition list itself

Syntax

It works very similar to the ordered and unordered list that come with the DokuWiki. All definition titles (“<dt>” in XHTML) start with a question mark “?” and all definition definitions (“<dd>” in XHTML) start with an exclamation mark “!”.

As in list, indentation is important. You can indent any number of spaces, even zero, for a definition list. If you indent more, you create a nested definition list inside the definition list.

Other than for lists, the definition list ends only after an empty line. This is necessary, because a definition text (title or definition) can be written on several lines.

Example

The following code:

     ? Definition Title
       Multiple lines are possible
     ! Definition Description
     ? Definition Title1
     ? Definition Title2
     ! Definition Description1
     ! Definition Description2
     ! Definition Description3
       ? Subdefinition
       ! Description
     ! Back
       ? Again deeper

!Can also start at first line with description only.

Is translated to:

<dl>
  <dt>Definition Title
      Multiple lines are possible</dt>
  <dd>Definition Description</dd>
  <dt>Definition Title1</dt>
  <dt>Definition Title2</dt>
  <dd>Definition Description1</dd>
  <dd>Definition Description2</dd>
  <dd>Definition Description3</dd>
  <dl>
    <dt>Subdefinition</dt>
    <dd>Description</dd>
  </dl>
  <dd>Back</dd>
  <dl>
    <dt>Again deeper</dt>
  </dl>
</dl>
 
<dl>
  <dd>Can also start at first line with description only.</dd>
</dl>

Installation

The Template's PHP Code

To install the functionality, copy the following code into a new file named lib/plugins/dl/syntax.php:

syntax.php
<?php
    if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
/** Declare Definition List.
 
    Declares a HTML - Definition-List
 
    Indentation: First either no indentation, or any number of spaces.
    Title: Line starts with (optional) spaces and a question mark
    Definition: Line starts with (opt.) spaces and an exclamation mark
    End: Multiple lines are allowed, so an empty line declares the end
    Subdefinitions: Indent more
 
    Syntax:
     ? Definition Title
       Multiple lines are possible
     ! Definition Description
     ? Definition Title1
     ? Definition Title2
     ! Definition Description1
     ! Definition Description2
     ! Definition Description3
       ? Subdefinition
       ! Description
     ! Back
       ? Again deeper
 
    License: LGPL
    */
class syntax_plugin_dl extends DokuWiki_Syntax_Plugin {
 
  function getInfo() {
    return array('author' => 'Marc Wäckerlin',
                 'email'  => 'marc [at] waeckerlin [dot-org]',
                 'name'   => 'Definition List Plugin',
                 'desc'   => 'HTML Definition List',
                 'url'    => 'http://marc.waeckerlin.org');
  }
 
  function getType() {
    return 'container';
  }
 
//   function getPType() {
//     return 'block';
//   }
 
  function getSort() {
    return 3;
  }
 
  function accepts($mode) {
    if (!count($this->allowedModes)) {
      global $PARSER_MODES;
      $this->allowedModes = array_merge($PARSER_MODES['container'],
                                        $PARSER_MODES['baseonly'],
                                        $PARSER_MODES['formatting'],
                                        $PARSER_MODES['substition'],
                                        $PARSER_MODES['protected'],
                                        $PARSER_MODES['disabled'],
                                        $PARSER_MODES['paragraphs']);
      unset($this->allowedModes[array_search('preformatted',
                                             $this->allowedModes)]);
    }
    return parent::accepts($mode);
  }
 
  function connectTo($mode) {
    $this->Lexer->addEntryPattern('^ *\!', $mode, 'plugin_dl');
    $this->Lexer->addEntryPattern('^ *\?[^\n]*', $mode, 'plugin_dl');
  }
 
  function postConnect() {
    $this->Lexer->addPattern('\n *\!', 'plugin_dl');
    $this->Lexer->addPattern('^ *\!', 'plugin_dl');
    $this->Lexer->addPattern('\n *\?[^\n]*', 'plugin_dl');
    $this->Lexer->addPattern('^ *\?[^\n]*', 'plugin_dl');
    $this->Lexer->addExitPattern('\n$', 'plugin_dl');
  }
 
  function handle($match, $state, $pos, &$handler){
    if (($state==DOKU_LEXER_MATCHED ||
         $state==DOKU_LEXER_ENTER)
        && ereg('\?', $match)) {
      $title =
        htmlspecialchars(ereg_replace("\n* *\?(.*)", '\1', $match));
      $match = ereg_replace("(\n* *\?).*", '\1', $match);
    } else {
      $title = '';
    }
    return array($match, $state, $title);
  }
 
  function render($format, &$renderer, $data) {
    static $close = '';
    static $level = 0;
    static $last_neesting = 0;
    static $indent = array();
    static $dlstart = "\n<dl>";
    static $dlend = "\n</dl>";
    static $dtstart = "\n<dt>";
    static $dtend = "</dt>";
    static $ddstart = "\n<dd>";
    static $ddend = "</dd>";
    list($match, $state, $title) = $data;
    while ($match[0]=="\n") $match=substr($match, 1);
    $neesting = strlen($match);
    if ($state==DOKU_LEXER_MATCHED) {
      if ($last_neesting<$neesting) {
        $renderer->doc .= $close.$ddstart.$dlstart;
        $close = '';
        $indent[++$level] = $neesting;
      } else if ($last_neesting>$neesting) {
        $renderer->doc .= $close.$dlend.($level?$ddend:'');
        $close = '';
        while ($level && $indent[--$level]>$neesting)
          $renderer->doc .= $dlend.($level?$ddend:'');
      }
      $last_neesting = $neesting;
    }
    switch ($state) {
      case DOKU_LEXER_ENTER: {
        $last_neesting = $neesting;
        if ($level>0) $renderer->doc .= $close;
        for (; $level>0; --$level)
          $renderer->doc .= $dlend.($level!=1?$ddend:'');
        $renderer->doc .= $dlstart;
        if (ereg('\?', $match)) {
          $renderer->doc .= $dtstart;
          $close = $dtend;
          if ($title!='')
            $renderer->doc .= '<a name="'.$title.'"></a>'.$title;
        } else if (ereg('!', $match)) {
          $renderer->doc .= $ddstart;
          $close = $ddend;
        } else return false;
        $indent[++$level] = $neesting;
      } return true;
      case DOKU_LEXER_EXIT: {
        $renderer->doc .= $close;
        for (; $level>0; --$level)
          $renderer->doc .= $dlend.($level!=1?$ddend:'');
        $renderer->doc .= "\n";
      } return true;
      case DOKU_LEXER_MATCHED: {
        if (ereg('\?', $match)) {
          $renderer->doc .= $close;
          $renderer->doc .= $dtstart;
          $close = $dtend;
          if ($title!='')
            $renderer->doc .= '<a class="target" name="'
              .$title.'"></a>'.$title;
        } else if (ereg('!', $match)) {
 
          $renderer->doc .= $close;
          $renderer->doc .= $ddstart;
          $close = $ddend;
        } else return false;
      } return true;
      case DOKU_LEXER_UNMATCHED: {
        $renderer->doc .= htmlspecialchars($match);
      } return true;
    }
    return false;
  }
}
?>

Optional CSS Definition

For the look and feel, put the following code in a new file lib/plugins/dl/style.css. Or optionally add the styles to your template's design.css file (if the name of your template is default, then it is in lib/tpl/default/design.css, also copy it to lib/tpl/default/print.css for the printing):

style.css
dl {
  padding: 0.1em;
  margin: 0.2em;
}
 
dl > dl {
  margin: 0em 0em 0em 4em;
  border: 0px;
  padding: 0px;
}
 
dt { 
  margin-top: .2em;
  font-weight: bold;
}
 
dt:before {  
  content: "→ ";
  font-weight: bold;
}
 
dt:after {  
  content: ":";
  font-weight: bold;
  margin-top: 0.2em;
}
 
dd {
  margin-bottom: .2em;
  margin-left: 4em;
}

Known Bugs

It is not XHTML valid, because it is nested inside a <p/>, where I have no influence on it. What can I do? (see also the discussion below)

  • With “Frusterick Manners” on PHP 5.3.3, it looked work well, but not with “Greebo” on PHP 5.6.21. After installed, the screen became white (blank). You could reload to redraw the page, but after that, every time you edit&save pages, the pages become blank. Simply invalidate/uninstall for restoration. It seems a PHP compatibility issue.

shintak 2018-08-26

Discussion

  • Neat, simple nesting, I should have put that in mine :-). You may want to include links to the alternate definition list plugins (there are three others) and add yours on the pages of those plugins.
  • Some coding notes.
    • The addPattern() method should really be moved from connectTo() to postConnect(). connectTo is called for each syntax mode and adds patterns to those modes. addPattern() is used to add patterns to your plugin's mode so only needs to be called once.
    • I take it you are not using getAllowedModes() and are overriding accepts() as you wish the plugin to be able to enter its own mode. However you are also using addPattern() to add the same patterns to your mode, which implies you want to handle them without nesting. That seems contradictory.
    • as an aside, should getAllowedModes() be updated to allow a plugin to easily indicate that it wishes to include itself in the list of syntax modes it accepts?

Christopher Smith 2006-01-12 13:39

Answer by Marc Wäckerlin

(There's no special “Discussion” button, is there?) In fact, it is my first try on writing a plugin for the DokuWiki, just to warm up. I've also written a cool page navigator, which is derived from the index, but that's not a plugin, but in the template's main.php. Is anyone interested? Where's the right way to publish? Also I added a “next” and “previous” page link in the navigator and the page header, so that one can step through all pages.

All I know, I got from the info-plugin sample, the online docs and code reading (especially the existing lists). Some things were hard to fiddle out, and for some things, I don't really know, why they are this way and not that way, so please educate me, when I'm wrong.

To your comments:

  1. Moved addPattern() to postConnect().
  2. I've overwritten accepts(), because I read somewhere that I should do it that way – if I remember right, I read that getAllowedModes() were deprecated? Anyway, it work's, and that's all I need ;-)
  3. That's a question? Well, where should I know from?
See the Syntax Plugins. The plugin interface has moved forward since its initial implementation, some of the older plugins may not take advantage of those changes. accepts() needed to be overridden until the September release when getAllowedModes() was introduced. getAllowedModes() and an accepts() method in the syntax plugin class overcame the original issue (the first plugin(s) loaded didn't see the syntax modes of plugins loaded later). Yes, it works, but it would work one way or the other, both aren't needed. I suspect you can do away with nesting. The last question was for debate, right now there is no answer just opinions ;-)
For your other functions, yes thats a problem. For now you could add something via the tips page. Tell me more about the index navigator thingy. It might dovetail well with the sidebar plugin, when no special sidebar page is provided.

Christopher Smith 2006-01-13 02:47

Hmm, did you ever bother to check whether the generated markup is valid? As far as I can see from the example given above at least the “nested” lists are not marked up correctly (the “nested” DL should be part of either DT or DD).

Actually, it can only be the DD. DT is quite restricted in what it can contain, much like a P. — Christopher Smith 2006-01-19 03:58

That problem, I have locally fixed and I'll upload as soon, as the following is fixed too:
Markup is incorrect for another reason too: The whole <dl> is placed inside a <p>, which is not allowed. Question: Why? What can I do to have <body> as parent? — Marc
Its a restriction on the <p> element. Strictly that means your plugin should be defined as PType=“block”, however block seems flaky. Checkout how the other plugins manage it. If I recall correctly, precede your plugin's markup with </p> and finish it with <p>. You should also have a look back at before adding the </p> to check for an empty <p> and if found remove it rather thank writing the closing tag. — Christopher Smith 2006-01-24 02:03
No, unfortunately that's a solution that inserts a new problem.
1. I don't want an empty paragraph in front and after. It is the last possibility to get it XHTML compliant, but not a good solution.
2. Returning 'block' in function getPType, resolves the problem of the <p> in front and </p> after, which are both gone, but unfortunately it introduces a new problem: All recognized elements within the <dl/>, e.g. <a/>-links are now enclosed in <p/>-paragraphs, which is absolutely not acceptable. I'd declare this as a bug of DokuWiki – What do you think?
3. Scanning and removing the preceding <p>, I suppose in the $renderer->doc, would work, even though it's a very dirty hack, but it's not a solution for the closing </p>, because I can't remove it in advance… — Marc

I don't seem to be able to get the multi-line DEFINITION TITLE to work. I copied the sample code, as is, ans multi-line definition title displays on a single line. — Walter 2008-01-11 02:20

It seems that DokuWiki links inside the definition title are not supported, in the definition definition they are. Is this intended or could this be improved? — Sven

plugin/dl.txt · Last modified: 2018-09-12 17:13 by shintak

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