DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:layout

Layout Plugin

Compatible with DokuWiki

2006-11-06

plugin Positioned block elements and styling for ad-hoc layout, columns etc.

Last updated on
2007-05-30
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.

Tagged with !experimental, boxes, style

Description

The Layout Plugin allows for positioned block elements to be added to wiki markup. In addition, block elements can have styles applied from an external .css stylesheet. The plugin is intended to add ad-hoc styling and layout capability to DokuWiki. Layout statements can be nested to provide more complex layouts.

NEW: (20070530)

  • html mangling fixed
  • now works with multiple styles
  • can choose styles based on page name
  • various other bugs removed

Syntax

<layout [l:N] [r:N] [t:N] [b:N] [w:N] [h:N] [z:N] [id:"ID"] [s:CLASS|"CLASSES..."] [s:"[!]PAGENAME[:]?CLASSNAME] [p:0|-]>
  wiki markup
</layout>
[p:-] position: relative; optional; Default is absolute positioning
[s:“style1 style2 …”} styles defined in layout.css optional
[s:“PAGENAME?CLASSNAME] class=“CLASSNAME” if on the PAGENAME page; trailing ”:“ checks if descendent optional
[s:”!PAGENAME?CLASSNAME] class=“CLASSNAME” if not on the PAGENAME pagetrailing “:” checks if descendent optional
[em] units; em, % optional; Default is pixels
[z:nn} z-index optional
[w:] width optional
[h:] height optional
[l:] left optional
[r:] right optional
[t:] top optional
[b:] bottom optional
[pl:] padding-left optional
[pr:] padding-right optional
[pt:] padding-top optional
[pb:] padding-bottom optional
[ml:] margin-left optional
[mr:] margin-right optional
[mt:] margin-top optional
[mb:] margin-bottom optional

Example Usage

simple column example

Layout uses shorthands for inline CSS markup.

Note that DW section heading markup does not work within layout tags.

<layout p:- w:20%>
some left column content
</layout>
<layout l:25% w:75%>
some right column content
</layout>

style selectors based on page name/namespace

<layout s:"plugin:layout?h1"> H1 </layout>

<div class=“H1”>H1</div>

<layout s:"plugin:someotherpage?h1"> Not H1 </layout>

<div>Not H1</div>

<layout s:"plugin:?h1"> H1 </layout>

<div class=“H1”>H1</div>

<layout s:"plugin?h1"> Not H1 </layout>

<div>Not H1</div>

Code Listing

syntax.php

<?php
/**
 * Layout Plugin: create absolute positioned layouts.
 * Version: 0.1alpha
 *
 * Syntax:     <layout [l:N] [r:N] [t:N] [b:N] [w:N] [h:N] [z:N] [id:"ID"] [s:CLASS|"CLASSES..."] [s:"[!]PAGENAME?CLASSNAME] [p:0|-]>...content...</layout>
 *
 * Example:    <layout r:30 w:60 t:40 z:100>
 * 
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Ivan A-R <ivalrom@gmail.com>
 * @author     Green Box <greenboxster@gmail.com>
 * @author 	 Luke Howson <mail@lukehowson.com>
 */
 
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');
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_layout extends DokuWiki_Syntax_Plugin {
 
    function getInfo(){
      return array(
        'author' => 'Ivan A-R',
        'email'  => 'ivalrom@gmail.com',
        'date'   => '2006-11-17',
        'name'   => 'Layout Plugin',
        'desc'   => 'create absolute positioned layouts',
        'url'    => 'http://iar.spb.ru/projects/dowkuwiki/layout',
      );
    }
 
    function getType(){ return 'container';}
    function getAllowedTypes() { return array('container', 'substition', 'protected', 'disabled', 'formatting', 'paragraphs'); }
    function getPType(){ return 'block';}
    function getSort(){ return 196; }
 
    // override default accepts() method to allow nesting
    // - ie, to get the plugin accepts its own entry syntax
    function accepts($mode) {
        if ($mode == substr(get_class($this), 7)) return true;
 
        return parent::accepts($mode);
    }
 
    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {
      $this->Lexer->addEntryPattern('<lo.*?>(?=.*?</lo.*?>)',$mode,'plugin_layout');
      $this->Lexer->addEntryPattern('<layout.*?>(?=.*?</layout.*?>)',$mode,'plugin_layout');
    }
 
    function postConnect() {
      $this->Lexer->addExitPattern('</lo.*?>', 'plugin_layout');
      $this->Lexer->addExitPattern('</layout.*?>', 'plugin_layout');
    }
 
    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
 
        switch ($state) {
            case DOKU_LEXER_ENTER:
                $data = (substr($match, 3, -1));
                return array(1, $data);
 
            case DOKU_LEXER_MATCHED:
                return array(2, $match);
 
            case DOKU_LEXER_UNMATCHED:                
                return array(3, $match);
 
            case DOKU_LEXER_EXIT:
                return array(4, '');
 
        }       
        return false;
    }
 
    /**
     * Create output
     */
    function render($mode, &$renderer, $indata) {
 
      list($instr, $data) = $indata;
 
      if('xhtml' == $mode){
        switch($instr) {
          case 1:
            $param = $this->_parse_param($data);
            $style = '';
            $class = '';
            $position = 'position:absolute; ';
            foreach($param as $k => $v) {
              switch(strtolower($k)) {
                case 'l': $style .= 'left:'.$this->_sufix($v, 'px').'; '; break;
                case 'r': $style .= 'right:'.$this->_sufix($v, 'px').'; '; break;
                case 't': $style .= 'top:'.$this->_sufix($v, 'px').'; '; break;
                case 'b': $style .= 'bottom:'.$this->_sufix($v, 'px').'; '; break;
                case 'w': $style .= 'width:'.$this->_sufix($v, 'px').'; '; break;
                case 'h': $style .= 'height:'.$this->_sufix($v, 'px').'; '; break;
                case 'z': $style .= 'z-index:'.$v.'; '; break;
                case 'id': $style .= 'id:'.$v.'; '; break;
                case 'pl': $style .= 'padding-left:'.$this->_sufix($v, 'px').'; '; break;
                case 'pr': $style .= 'padding-right:'.$this->_sufix($v, 'px').'; '; break;
                case 'pt': $style .= 'padding-top:'.$this->_sufix($v, 'px').'; '; break;
                case 'pb': $style .= 'padding-bottom:'.$this->_sufix($v, 'px').'; '; break;
                case 'ml': $style .= 'margin-left:'.$this->_sufix($v, 'px').'; '; break;
                case 'mr': $style .= 'margin-right:'.$this->_sufix($v, 'px').'; '; break;
                case 'mt': $style .= 'margin-top:'.$this->_sufix($v, 'px').'; '; break;
                case 'mb': $style .= 'margin-bottom:'.$this->_sufix($v, 'px').'; '; break;
                case 'p':
                  switch($v) {
                    case '-': $position = 'position:relative; '; break;
                    case 0: $position = ''; break; // none
                    }
                    break;
                case 's':
                    $instructions = split(' ', $v);
                    foreach ($instructions as $instruction) {
                        $instruction = trim($instruction, ' "');
                        if (preg_match("/\?/", $instruction)) {
                            $out = $this->_outputClass($instruction);
                        }
                        else { $out = $instruction; }
                        if($out != "") {
                            if($class != "") $class .= " ";
                            $class .= $out;
                        }
                    }                    
                    break;
              }
            }
            $renderer->doc .= "<div";
            if ($class) $renderer->doc .= ' class="'.$class.'"';
            if ($style) $renderer->doc .= ' style="'.$position.$style.'"';
            $renderer->doc .= '>'; 
            break;
          case 2: case 3: $renderer->doc .= $data; break;
          case 4: $renderer->doc .= "</div>"; break;
        } 
        return true;
      }
      return false;
    }
 
    function _outputClass($instruction)
    {
        // $matchType points to _eq for PAGE?CLASS syntax and
        // _isAncestor for NAMESPACE:?CLASS syntax
        if(preg_match('/:\?/', $instruction) ) { $matchType = "_isAncestor"; }
        else { $matchType = "_eq"; }
        if($this->_hasA($instruction, "?") ) {
            if($this->_hasA($instruction, "!")) {
                if ( $this->$matchType($this->_pageName($instruction), getID() ) ) {
                    return "";
                }
            }
            else if (!($this->$matchType($this->_pageName($instruction), getID() ) ) ) {
                return "";
            }
        }
        return $this->_className($instruction);
    }
 
    // named equality operator
    function _eq($a, $b) {
        return ($a == $b);
    }
 
    function _isAncestor($ancestor, $descendent) {
        $ancestor = substr($ancestor, 0, strlen($ancestor) - 1);
        $anc = split (':', $ancestor);
        $desc = split (':', $descendent);
        if(sizeof($anc) > sizeof($desc) ) { return false; }
        for ($i = 0; $i != sizeof($anc); $i++) {
            if ( $anc[$i] != $desc[$i] ) {return false;}
        }
        return true;
    }
 
    // Returns the page name in an "s:[!]PAGENAME?CLASSNAME" string
    function _pageName($str) {
        $R = array();
        preg_match('/!?(.*?)\\?/', $str, $R);
        return $R[1];
    }
 
    // Returns the class name in an "s:[!]PAGENAME?CLASSNAME" string
    function _className($str) {
        $R = array();
        preg_match("/\\?(.*)/", $str, $R);
        return $R[1];
    }
 
    // sees if a string has a particular character
    function _hasA($str, $char) {
        return !(strpos($str, $char) === FALSE );   
    }
 
    function _parse_param($str) {
        $R = array();
        preg_match_all('/(\\w+):("[^"]*"|\\S*)|(\\w+)/', $str, $R);
        $keys = array();
        $option = 1;
        $quotedValue = 2;
        $unquotedValue = 3;
        foreach($R[0] as $k => $v) {
            if($R[$option][$k]) {
                $keys[$R[$option][$k]] = $R[$quotedValue][$k];
            } else {
                // ?
                $keys[$R[$unquotedValue][$k]] = TRUE;
            }
        }
        return $keys;
    }
 
    function _sufix($val, $sufix) {
      if(preg_match('/^\-?[0-9]+$/', $val)) { // If only numeric value
        return $val.$sufix; // Add default suffix
      } else {
        return $val;
      }
    }
 
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :s

style.css

* Example Styles for Layout plugin for DokuWiki
 *
 * @author Ivan A-R  <ivalrom@gmail.com>
 * @author Green Box <greenboxster@gmail.com>
 *
 * Styles to be applied by layout plugin.
 * Modify to suit...
 */
 
div.red {
 background-color: #FFFFCC;
  font-size: 12pt;
}
 
div.lay
  border: 1px solid red;
  padding: 0.5em;
}
 
div.layout2 {
  font-size: 8pt;
}
 
div.centrec {
  padding-left:200px;
  padding-right:150px;
  }
 
div.backred {
  background-color: #FFFFCC;
}
 
div.textbig p {
  color:red;
  font-size:120%;
}

To Do

  • respect DokuWiki heading syntax
  • add optional section edit button for positioned elements

Bugs

Very nice indeed!

However, I have two comments/bugs to mention:

First, the nomenclature for specifying the class seems to be

<layout s:myclass>
Foo Bar
</layout>

and not st:myclass.

Second, the corresponding HTML has an extra whitespace inserted:

<div class=" myclass">
<p>
Foo Bar
</p>
</div>

– Niklas Volbers

Fixed these problems
– Luke Howson


Bug or something:
When in order to render two columns you give

<layout w:35%>
...
</layout>
<layout l:36% w:63%>
...
</layout>

the menu bar is strangely rendered twice.

However if you put the second column relative it renders right.

<layout w:35%>
...
</layout>
<layout l:36% w:63% p:->
...
</layout>

– H.P. 2008-05-18

Comments

Thanks to Chris Smith for advice.

Erm.. How does one install it? I'm only interested in the columns feature; is that working? And how does one use it; there are no examples for setting up columns. Thanks!

Manual install, as described here: installation Bernd

The example above with two columns works with IE, but with Fire Fox there is a problem: The second column starts about half a row below the first column. StinkyWinky 2008/08/06 10:11

plugin/layout.txt · Last modified: 2014-03-30 19:32 by Aleksandr