DokuWiki

It's better when it's simple

User Tools

Site Tools


Sidebar

Translations of this page?:

Learn about DokuWiki

Advanced Use

Corporate Use

Our Community


Follow us on Facebook, Twitter and other social networks.

tips:tables_with_rowspan

Tables with rowspans and vertical alignment

Introduction

Having examined the two current (Oct08) offerings to facilitate rowspans in a DW table (see Method 1 and Method 2), I felt that what was needed was a simpler way of including the necessary markup without losing the clean and elegant DW table syntax. While a syntax plugin would have been my preferred way to achieve this, I was unable, with my limited knowledge of PHP, to determine how to insert the appropriate code inside a TH or TD HTML tag. This offering therefore is based on the approach used by jmucchiello in Method 2 but with a more direct way of specifying the rowspan syntax.

By applying the code patches listed below, it is possible to create a table with rowspans, each with their own vertical alignment. The patches do not alter the existing table syntax. To create a rowspan, the table cell starting the rowspan must include the character “:” followed by the number of rows to be spanned, followed optionally by the character “-” to indicate middle (or central) vertical alignment or the character “_” to indicate bottom vertical alignment. The absence of this optional vertical alignment character would indicate vertical alignment at the top.

Using this syntax means that the number of columns in succeeding rows will be reduced, depending on how many previous rowspans affect any particular row.

Features at a glance

  • 100% compatible with existing DokuWiki tables.
  • Supports bidirectional spans - column spans can be incorporated with rowspans.
  • Supports vertical alignment (top, middle and bottom) in each rowspan using CSS.
  • Supports both header and non-header spans.

Usage

example table To generate this example table in Dokuwiki, the following syntax would be used:

^ ^col 1 ^  col 2  ^  col 3&4^^
^row a  |:2 a1-b1  |  :3_ a2-c3|| a4 |
^row b&c :2- | b4|
^c1  ^ c4|
^row d  |d1  |  d2  |  d3| d4 |

This 4×4 table (plus headers) has individual cells at a4, b4, c1, c4, d1, d2, d3, and d4.
The top row and leftmost columns are header cells. c1 and c4 are also header cells.
There are 2 2-cell blocks, the one at rowb&c is aligned vertically in the middle and the one at a1-b1 is top aligned.
There is a 6-cell block spanning a2, a3, b2, b3, c2 and c3 which is bottom aligned vertically.

Note that the order in which a cell's content and any row spanning statement are coded is not important, either may appear first. The HTML generated by the above DW table definitions would be:

<table class="inline">
   <tr class="row0">
      <th class="col0"> </th><th class="col1">col 1 </th><th class="col2 centeralign">  col 2  </th><th class="col3 rightalign" colspan="2">  col 3&amp;4</th>
   </tr>
   <tr class="row1">
      <th class="col0 leftalign">row a  </th><td class="col1 leftalign valigntop" rowspan="2"> a1-b1  </td><td class="col2 rightalign valignbot" rowspan="3" colspan="2">   a2-c3</td><td class="col4"> a4 </td>
   </tr>
   <tr class="row2">
      <th class="col0 valignmid" rowspan="2">row b&amp;c  </th><td class="col1"> b4</td>
   </tr>
   <tr class="row3">
      <th class="col0 leftalign">c1  </th><th class="col1 rightalign">  c4</th>
   </tr>
   <tr class="row4">
      <th class="col0 leftalign">row d  </th><td class="col1 leftalign">d1  </td><td class="col2 centeralign">  d2  </td><td class="col3 rightalign">  d3</td><td class="col4"> d4 </td>
   </tr>
</table>

Known Issues

  • The column number in the class= generated statement will not be correct when any column in the row is an intermediate vertically spanned column, see the cell containing the value b4 in the example above. BTW, this column number was not computed correctly for any cells following a horizontal span (Bug#1506 in DW version 05-05-2008). The patch below corrects this oversight.
  • Care is needed when all columns in a row have no visible content. Some browsers will suppress the display of such a row and although there is a CSS rule {empty-cells:show} it is not supported by all browsers (eg IE). If all the columns in a row are intermediate vertically spanned columns, then there are no cells to define in the row, but an opening / closing TR tag is still required.
  • An update to the wiki:syntax page for table formatting using rowspan is outstanding.

Patch Code

The patch was introduced into the Dokuwiki release dated 2008-05-05. It has been tested and works under the current Dokuwiki release dated 2009-02-14 (Jan/2009-03-22).

The patch involves changes to 5 code files in the inc/parser directory. They are parser.php, handler.php, metadata.php, renderer.php, and xhtml.php. In addition, the CSS rules are shown for inclusion in the userstyle.css file in the conf directory.

parser.php

in the class Doku_Parser_Mode_table, find the function postConnect() which begins at LINE 453, and then incorporate the following one line addition at the position marked:

     $this->Lexer->addPattern('[\t ]+','table');
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
     $this->Lexer->addPattern(':(?:[2-9][0-9]?|[1][0-9])[-_]?','table');
/*** ------------------------------------------------------------ ROWSPAN: addition end */
     $this->Lexer->addPattern('\^','table');

handler.php

There are 4 separate changes to be made to this file.

Change 1

In the class Doku_Handler, find the function table($match, $state, $pos) which begins at LINE 580 and then the case DOKU_LEXER_MATCHED which begins at LINE 610. Finally, incorporate the following two line addition at the position marked:

     } else if ( $match == '^' ) {
         $this->_addCall('tableheader', array(), $pos);
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
     } else if ( substr($match,0,1) == ':') {
         $this->_addCall('tablevspan', array($match), $pos);
/*** ------------------------------------------------------------ ROWSPAN: addition end */
     }

Change 2

In the class Doku_Handler_Table find the function process() which begins at LINE 1190 and then incorporate the following three line addition at the position marked:

     case 'tableheader':
     case 'tablecell':
         $this->tableCell($call);
         break;
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
     case 'tablevspan':
         $this->tableVspan($call);
         break;
/*** ------------------------------------------------------------ ROWSPAN: addition end */
     case 'table_end':

Change 3

Still in the class Doku_Handler_Table find the function tableDefault($call) which begins at LINE 1295 and then incorporate the following three line addition at the position marked:

    function tableDefault($call) {
        $this->tableCalls[] = $call;
    }
 
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
    function tableVspan($call) {
        $this->tableCalls[] = array('rowspan',$call[1],$call[2]);
    }
/*** ------------------------------------------------------------ ROWSPAN: addition end */

Change 4

And finally, still in the class Doku_Handler_Table, find the function finalizeTable() which begins at LINE 1306 and then incorporate the following fifteen line addition at the position marked:

        $toDelete[] = $key-1;
        $toDelete[] = $key;
        $toDelete[] = $key+1;
 
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
     } else if ( $call[0] == 'rowspan' ) {
        $match = $call[1][0];
        $tmpa = substr($match,1,strlen($match)-1);
        $valign = substr($tmpa,-1,1);
        if ($valign == "-") {
           $valign = 'mid';
           $tmpa = substr($tmpa,0,strlen($tmpa)-1);
        } else if ($valign == "_") {
           $valign = 'bot';
           $tmpa = substr($tmpa,0,strlen($tmpa)-1);
        }
        else $valign = 'top';
        $this->tableCalls[$lastCell][1][2] = $tmpa;
        $this->tableCalls[$lastCell][1][3] = $valign;
        $toDelete[] = $key;
/*** ------------------------------------------------------------ ROWSPAN: addition end */

metadata.php

There are 2 changes to be made to this file.

In the class Doku_Renderer_metadata find the function tableheader_open which begins at LINE 364 and then incorporate the following one line replacement:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: change start */
  function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){}
  //function tableheader_open($colspan = 1, $align = NULL){}
/*** ------------------------------------------------------------ ROWSPAN: change end */

and a little further on, find the function tablecell_open and incorporate the following one line replacement:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: change start */
  function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){}
  //function tablecell_open($colspan = 1, $align = NULL){}
/*** ------------------------------------------------------------ ROWSPAN: change end */

renderer.php

There are 2 changes to be made to this file in a similar way to metadata.php.

In the class Doku_Renderer find the function tableheader_open which begins at LINE 241 and then incorporate the following one line replacement:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: change start */
  function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){}
  //function tableheader_open($colspan = 1, $align = NULL){}
/*** ------------------------------------------------------------ ROWSPAN: change end */

and a little further on, find the function tablecell_open and incorporate the following one line replacement:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: change start */
  function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){}
  //function tablecell_open($colspan = 1, $align = NULL){}
/*** ------------------------------------------------------------ ROWSPAN: change end */

xhtml.php

There are 2 changes to be made to this file.

In the class Doku_Renderer_xhtml find the function tableheader_open which begins at LINE 824 and then incorporate the following replacement for this function:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: replacement start */
   function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){
      $class = 'class="col' . $this->_counter['cell_counter']++;
      if ( !is_null($align) ) {
         $class .= ' '.$align.'align';
      }
      if ( !is_null($valign) ) {
         $class .= ' valign'.$valign;
      }
      $class .= '"';
      $this->doc .= '<th ' . $class;
      if ( $rowspan > 1 ) {
         $this->doc .= ' rowspan="'.$rowspan.'"';
      }
      if ( $colspan > 1 ) {
         $this->_counter['cell_counter'] += $colspan-1;    /*--- BUG FS#1506  */
         $this->doc .= ' colspan="'.$colspan.'"';
      }
      $this->doc .= '>';
   }
/*** ------------------------------------------------------------ ROWSPAN: replacement end */

and a little further on, find the function tablecell_open and incorporate the following replacement for this function:

/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: replacement start */
   function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1, $valign = NULL){
      $class = 'class="col' . $this->_counter['cell_counter']++;
      if ( !is_null($align) ) {
         $class .= ' '.$align.'align';
      }
      if ( !is_null($valign) ) {
         $class .= ' valign'.$valign;
      }
      $class .= '"';
      $this->doc .= '<td ' . $class;
      if ( $rowspan > 1 ) {
         $this->doc .= ' rowspan="'.$rowspan.'"';
      }
      if ( $colspan > 1 ) {
         $this->_counter['cell_counter'] += $colspan-1;    /*--- BUG FS#1506  */
         $this->doc .= ' colspan="'.$colspan.'"';
      }
      $this->doc .= '>';
   }
/*** ------------------------------------------------------------ ROWSPAN: replacement end */

userstyle.css

And finally, the CSS is added to the conf/userstyle.css, which if it does not exist, may be created as a simple text file.

/* for table cell vertical alignment   */
.valigntop {vertical-align:top}
.valignmid {vertical-align:middle}
.valignbot {vertical-align:bottom}

Comments / Discussion

Feel free to express your opinion. — Jan Weatherhead 2008-10-14


Hi, it works very good, many thanks! PS look here to see.

Frank


Hi Jan,

Very nice and useful patch! However, I am not sure that this way of integrating rowspan is really 100% compatible. What worries me is that the expression :n can occur anywhere in the cell and still means “rowspan”.
I would prefer if rowspan functionality will be invoked only when the expression directly follows the “|” or “^” without anything in betweeen, including space. As dokuwiki is being used in technical documentations, I can easily image table uses when there is a “:” followed by a number somewhere in a table cell.

My understanding of the whole parser is still not very good.
But the following modification to Change 4 of inc/parser/handler.php seems to work for me:

        $toDelete[] = $key-1;
        $toDelete[] = $key;
        $toDelete[] = $key+1;
 
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
     } else if ( $call[0] == 'rowspan' ) {
	 // Apply only immediately after a cell open
         if ( $this->tableCalls[$key-1][0] == 'tablecell_open' || $this->tableCalls[$key-1][0] == 'tableheader_open' ) {
            $match = $call[1][0];
            $tmpa = substr($match,1,strlen($match)-1);
            $valign = substr($tmpa,-1,1);
            if ($valign == "-") {
               $valign = 'mid';
               $tmpa = substr($tmpa,0,strlen($tmpa)-1);
            } else if ($valign == "_") {
               $valign = 'bot';
               $tmpa = substr($tmpa,0,strlen($tmpa)-1);
            }
            else $valign = 'top';
            $this->tableCalls[$lastCell][1][2] = $tmpa;
            $this->tableCalls[$lastCell][1][3] = $valign;
            $toDelete[] = $key;
        }
        else {
            // Convert the false-positive back to cdata
            $this->tableCalls[$key][0] = 'cdata';
        }
/*** ------------------------------------------------------------ ROWSPAN: addition end */

Is it a good idea or a bad one? Astrid Hanssen 2008-10-25

Many thanks for your feedback, Astrid. I realise on reflection just how exaggerated the claim of 100% compatibility is. And I believe that fixing the position of the rowspan markup to follow immediately after the cell start markup as you suggest, is on balance a small step in the right direction towards that 100% target.

However, implementing your changes has an immediate side-effect on the horizontal alignment of data in the rowspanned cells - they are always left aligned. To overcome this drawback and re-establish the DW rules on horizontal alignment in cells, the following additional patch is required.

Patch 3a

Still in the class Doku_Handler_Table find the function finalizeTable() which begins at LINE 1306 and then incorporate the following two line addition at the position marked:
      } else if ( $call[0] == 'table_align' ) {
 
      // If the previous element was a cell open, align right
         if ( $this->tableCalls[$key-1][0] == 'tablecell_open' || $this->tableCalls[$key-1][0] == 'tableheader_open' ) {
            $this->tableCalls[$key-1][1][1] = 'right';
 
/*** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ROWSPAN: addition start */
         } else if ( $this->tableCalls[$key-1][0] == 'rowspan' ) {
            $this->tableCalls[$key-2][1][1] = 'right';
/*** ------------------------------------------------------------ ROWSPAN: addition end */
Now the only loss of compatibility with existing DW table markup would be when :n data is used in a left-aligned cell. The solution here would be an enforced change to the existing DW table cell data to wrap %% around this data in order to ignore the markup. Hence I feel the best that can be said about compatibility and this modification is that it approaches 100%.
Jan/2008-10-28

I failed at first (probably made a copy paste error), but then tried again after restoring and it worked. I incorporated the Astrid patch as well, since I like stricter syntax and can sense data with colons and numbers coming on some day. Though I won't be using it much, I sure missed rowspans at several points in my life of using DokuWiki.
Thank you very much. — Doc/2009-09-03

tips/tables_with_rowspan.txt · Last modified: 2016-03-05 23:18 by 2602:306:cf53:d1a0:bcdf:f810:e342:e4d1