includes/wikiengine/Parse/Mediawiki/Table.php
changeset 1027 98c052fc3337
parent 1026 f0431eb8161e
child 1028 dde4416dea00
equal deleted inserted replaced
1026:f0431eb8161e 1027:98c052fc3337
     1 <?php
       
     2 // vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
       
     3 /**
       
     4  * Mediawiki: Parses for tables.
       
     5  *
       
     6  * This class implements a Text_Wiki_Rule to find tables in pipe syntax
       
     7  * {| ... |- ... | ... |}
       
     8  * On parsing, the text itself is left in place, but the starting and ending
       
     9  * tags for table, rows and cells are replaced with tokens. (nested tables enabled)
       
    10  *
       
    11  * PHP versions 4 and 5
       
    12  *
       
    13  * @category   Text
       
    14  * @package    Text_Wiki
       
    15  * @author     Bertrand Gugger <bertrand@toggg.com>
       
    16  * @copyright  2005 bertrand Gugger
       
    17  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
       
    18  * @version    CVS: $Id: Table.php,v 1.7 2005/12/06 15:54:56 ritzmo Exp $
       
    19  * @link       http://pear.php.net/package/Text_Wiki
       
    20  */
       
    21 
       
    22 /**
       
    23  * Table rule parser class for Mediawiki.
       
    24  *
       
    25  * @category   Text
       
    26  * @package    Text_Wiki
       
    27  * @author     Bertrand Gugger <bertrand@toggg.com>
       
    28  * @copyright  2005 bertrand Gugger
       
    29  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
       
    30  * @version    Release: @package_version@
       
    31  * @link       http://pear.php.net/package/Text_Wiki
       
    32  * @see        Text_Wiki_Parse::Text_Wiki_Parse()
       
    33  */
       
    34 class Text_Wiki_Parse_Table extends Text_Wiki_Parse {
       
    35 
       
    36     /**
       
    37      * The regular expression used to parse the source text and find
       
    38      * matches conforming to this rule.  Used by the parse() method.
       
    39      *
       
    40      * @access public
       
    41      * @var string
       
    42      * @see parse()
       
    43      */
       
    44     var $regex = '#^\{\|(.*?)(?:^\|\+(.*?))?(^(?:((?R))|.)*?)^\|}#msi';
       
    45 
       
    46     /**
       
    47      * The regular expression used in second stage to find table's rows
       
    48      * used by process() to call back processRows()
       
    49      *
       
    50      * @access public
       
    51      * @var string
       
    52      * @see process()
       
    53      * @see processRows()
       
    54      */
       
    55     var $regexRows = '#(?:^(\||!)-|\G)(.*?)^(.*?)(?=^(?:\|-|!-|\z))#msi';
       
    56 
       
    57     /**
       
    58      * The regular expression used in third stage to find rows's cells
       
    59      * used by processRows() to call back processCells()
       
    60      *
       
    61      * @access public
       
    62      * @var string
       
    63      * @see process()
       
    64      * @see processCells()
       
    65      */
       
    66     var $regexCells =
       
    67     '#((?:^\||^!|\|\||!!|\G))(?:([^|\n]*?) \|(?!\|))?(.+?)(?=^\||^!|\|\||!!|\z)#msi';
       
    68 
       
    69     /**
       
    70      * The current table nesting depth, starts by zero
       
    71      *
       
    72      * @access private
       
    73      * @var int
       
    74      */
       
    75     var $_level = 0;
       
    76 
       
    77     /**
       
    78      * The count of rows for this level
       
    79      *
       
    80      * @access private
       
    81      * @var array of int
       
    82      */
       
    83     var $_countRows = array();
       
    84 
       
    85     /**
       
    86      * The max count of cells for this level
       
    87      *
       
    88      * @access private
       
    89      * @var array of int
       
    90      */
       
    91     var $_maxCells = array();
       
    92 
       
    93     /**
       
    94      * The count of cells for each row
       
    95      *
       
    96      * @access private
       
    97      * @var array of int
       
    98      */
       
    99     var $_countCells = array();
       
   100 
       
   101     /**
       
   102      * The count of spanned cells from previous rowspans for each column
       
   103      *
       
   104      * @access private
       
   105      * @var array of int
       
   106      */
       
   107     var $_spanCells = array();
       
   108 
       
   109     /**
       
   110      * Generates a replacement for the matched text. Returned token options are:
       
   111      * 'type' =>
       
   112      *     'table_start'   : the start of a bullet list
       
   113      *     'table_end'     : the end of a bullet list
       
   114      *     'row_start'     : the start of a number list
       
   115      *     'row_end'       : the end of a number list
       
   116      *     'cell_start'    : the start of item text (bullet or number)
       
   117      *     'cell_end'      : the end of item text (bullet or number)
       
   118      *     'caption_start' : the start of associated caption
       
   119      *     'caption_end'   : the end of associated caption
       
   120      *
       
   121      * 'level' => the table nesting level (starting zero) ('table_start')
       
   122      *
       
   123      * 'rows' => the number of rows in the table ('table_start')
       
   124      *
       
   125      * 'cols' => the number of columns in the table or rows
       
   126      *           ('table_start' and 'row_start')
       
   127      *
       
   128      * 'span' => column span ('cell_start')
       
   129      *
       
   130      * 'row_span' => row span ('cell_start')
       
   131      *
       
   132      * 'attr' => header optional attribute flag ('row_start' or 'cell_start')
       
   133      *
       
   134      * 'format' => table, row or cell optional styling ('xxx_start')
       
   135      *
       
   136      * @param array &$matches The array of matches from parse().
       
   137      * @return string the original text with tags replaced by delimited tokens
       
   138      * which point to the the token array containing their type and definition
       
   139      * @access public
       
   140      */
       
   141     function process(&$matches)
       
   142     {
       
   143         if (array_key_exists(4, $matches)) {
       
   144             $this->_level++;
       
   145             $expsub = preg_replace_callback(
       
   146                 $this->regex,
       
   147                 array(&$this, 'process'),
       
   148                 $matches[3]
       
   149             );
       
   150             $this->_level--;
       
   151         } else {
       
   152             $expsub = $matches[3];
       
   153         }
       
   154         $this->_countRows[$this->_level] = $this->_maxCells[$this->_level] = 0;
       
   155         $this->_countCells[$this->_level] = $this->_spanCells[$this->_level] = array();
       
   156         $sub = preg_replace_callback(
       
   157             $this->regexRows,
       
   158             array(&$this, 'processRows'),
       
   159             $expsub
       
   160         );
       
   161         $param = array(
       
   162                 'type'  => 'table_start',
       
   163                 'level' => $this->_level,
       
   164                 'rows' => $this->_countRows[$this->_level],
       
   165                 'cols' => $this->_maxCells[$this->_level]
       
   166         );
       
   167         if ($format = trim($matches[1])) {
       
   168             $param['format'] = $format;
       
   169         }
       
   170         $ret = $this->wiki->addToken($this->rule, $param );
       
   171         if ($matches[2]) {
       
   172             $ret .= $this->wiki->addToken($this->rule, array(
       
   173                 'type'  => 'caption_start',
       
   174                 'level' => $this->_level ) ) . $matches[2] .
       
   175                     $this->wiki->addToken($this->rule, array(
       
   176                 'type'  => 'caption_end',
       
   177                 'level' => $this->_level ) );
       
   178         }
       
   179         $param['type'] = 'table_end';
       
   180         return $ret . $sub . $this->wiki->addToken($this->rule, $param );
       
   181     }
       
   182 
       
   183     /**
       
   184      * Generates a replacement for the matched rows. Token options are:
       
   185      * 'type' =>
       
   186      *     'row_start'   : the start of a row
       
   187      *     'row_end'     : the end of a row
       
   188      *
       
   189      * 'order' => the row order in the table
       
   190      *
       
   191      * 'cols' => the count of cells in the row
       
   192      *
       
   193      * 'attr' => header optional attribute flag
       
   194      *
       
   195      * 'format' => row optional styling
       
   196      *
       
   197      * @param array &$matches The array of matches from process() callback.
       
   198      * @return string 2 delimited tokens pointing the row params
       
   199      * and containing the cells-parsed block of text between the tags
       
   200      * @access public
       
   201      */
       
   202     function processRows(&$matches)
       
   203     {
       
   204         $this->_countCells[$this->_level][$this->_countRows[$this->_level]] = 0;
       
   205         $sub = preg_replace_callback(
       
   206             $this->regexCells,
       
   207             array(&$this, 'processCells'),
       
   208             $matches[3]
       
   209         );
       
   210         $param = array(
       
   211                 'type'  => 'row_start',
       
   212                 'order' => $this->_countRows[$this->_level],
       
   213                 'cols' => $this->_countCells[$this->_level][$this->_countRows[$this->_level]++]
       
   214         );
       
   215         if ($matches[1] == '!') {
       
   216             $param['attr'] = 'header';
       
   217         }
       
   218         if ($format = trim($matches[2])) {
       
   219             $param['format'] = $format;
       
   220         }
       
   221         if ($this->_maxCells[$this->_level] < $param['cols']) {
       
   222             $this->_maxCells[$this->_level] = $param['cols'];
       
   223         }
       
   224         $ret = $this->wiki->addToken($this->rule, $param );
       
   225         $param['type'] = 'row_end';
       
   226         return $ret . $sub . $this->wiki->addToken($this->rule, $param );
       
   227     }
       
   228 
       
   229     /**
       
   230      * Generates a replacement for the matched cells. Token options are:
       
   231      * 'type' =>
       
   232      *     'cell_start'   : the start of a row
       
   233      *     'cell_end'     : the end of a row
       
   234      *
       
   235      * 'order' => the cell order in the row
       
   236      *
       
   237      * 'cols' => the count of cells in the row
       
   238      *
       
   239      * 'span' => column span
       
   240      *
       
   241      * 'row_span' => row span
       
   242      *
       
   243      * 'attr' => header optional attribute flag
       
   244      *
       
   245      * 'format' => cell optional styling
       
   246      *
       
   247      * @param array &$matches The array of matches from processRows() callback.
       
   248      * @return string 2 delimited tokens pointing the cell params
       
   249      * and containing the block of text between the tags
       
   250      * @access public
       
   251      */
       
   252     function processCells(&$matches)
       
   253     {
       
   254         $order = & $this->_countCells[$this->_level][$this->_countRows[$this->_level]];
       
   255         while (isset($this->_spanCells[$this->_level][$order])) {
       
   256             if (--$this->_spanCells[$this->_level][$order] < 2) {
       
   257                 unset($this->_spanCells[$this->_level][$order]);
       
   258             }
       
   259             $order++;
       
   260         }
       
   261         $param = array(
       
   262                 'type'  => 'cell_start',
       
   263                 'attr'  => $matches[1] && ($matches[1]{0} == '!') ? 'header': null,
       
   264                 'span'  => 1,
       
   265                 'rowspan'  => 1,
       
   266                 'order' => $order
       
   267         );
       
   268         if ($format = trim($matches[2])) {
       
   269             if (preg_match('#(.*)colspan=("|\')?(\d+)(?(2)\2)(.*)#i', $format, $pieces)) {
       
   270                 $param['span'] = (int)$pieces[3];
       
   271                 $format = $pieces[1] . $pieces[4];
       
   272             }
       
   273             if (preg_match('#(.*)rowspan=("|\')?(\d+)(?(2)\2)(.*)#i', $format, $pieces)) {
       
   274                 $this->_spanCells[$this->_level][$order] =
       
   275                                     $param['rowspan'] = (int)$pieces[3];
       
   276                 $format = $pieces[1] . $pieces[4];
       
   277             }
       
   278             $param['format'] = $format;
       
   279         }
       
   280         $this->_countCells[$this->_level][$this->_countRows[$this->_level]] += $param['span'];
       
   281         $ret = $this->wiki->addToken($this->rule, $param);
       
   282         $param['type'] = 'cell_end';
       
   283         return $ret . $matches[3] . $this->wiki->addToken($this->rule, $param );
       
   284     }
       
   285 }
       
   286 ?>