1<?php 2// vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: 3/** 4 * BBCode: Parses for code blocks. 5 * 6 * This class implements a Text_Wiki_Rule to find source text marked as 7 * bulleted or numbered lists as defined by text surrounded by [list] [*] ... [/list] 8 * Numebering is obtained thru [list=1] or [list=a] defining the first item "number" 9 * On parsing, the text itself is left in place, but the starting, element and ending 10 * tags are replaced with tokens. (nested lists enabled) 11 * 12 * PHP versions 4 and 5 13 * 14 * @category Text 15 * @package Text_Wiki 16 * @author Bertrand Gugger <bertrand@toggg.com> 17 * @copyright 2005 bertrand Gugger 18 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 19 * @version CVS: $Id$ 20 * @link http://pear.php.net/package/Text_Wiki 21 */ 22 23/** 24 * List rule parser class for BBCode. 25 * 26 * @category Text 27 * @package Text_Wiki 28 * @author Bertrand Gugger <bertrand@toggg.com> 29 * @copyright 2005 bertrand Gugger 30 * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 31 * @version Release: @package_version@ 32 * @link http://pear.php.net/package/Text_Wiki 33 * @see Text_Wiki_Parse::Text_Wiki_Parse() 34 */ 35class Text_Wiki_Parse_List extends Text_Wiki_Parse { 36 37 /** 38 * The regular expression used to parse the source text and find 39 * matches conforming to this rule. Used by the parse() method. 40 * 41 * @access public 42 * @var string 43 * @see parse() 44 */ 45 var $regex = "#\[list(?:=(.+?))?]\n?((?:((?R))|.)*?)\[/list]\n?#msi"; 46 47 /** 48 * The regular expression used in second stage to find list's elements 49 * used by process() to call back processElement() 50 * 51 * @access public 52 * @var string 53 * @see process() 54 * @see processElement() 55 */ 56 var $regexElement = '#\[\*](.*?)(?=\[\*]|$)\n?#msi'; 57 58 /** 59 * The current list nesting depth, starts by zero 60 * 61 * @access private 62 * @var int 63 */ 64 var $_level = 0; 65 66 /** 67 * The count of items for this level 68 * 69 * @access private 70 * @var int 71 */ 72 var $_count = array(); 73 74 /** 75 * The type of list for this level ('bullet' or 'number') 76 * 77 * @access private 78 * @var int 79 */ 80 var $_type = array(); 81 82 /** 83 * Generates a replacement for the matched text. Returned token options are: 84 * 'type' => 85 * 'bullet_list_start' : the start of a bullet list 86 * 'bullet_list_end' : the end of a bullet list 87 * 'number_list_start' : the start of a number list 88 * 'number_list_end' : the end of a number list 89 * 'item_start' : the start of item text (bullet or number) 90 * 'item_end' : the end of item text (bullet or number) 91 * 'unknown' : unknown type of list or item 92 * 93 * 'level' => the indent level (0 for the first level, 1 for the 94 * second, etc) 95 * 96 * 'count' => the list item number at this level. not needed for 97 * xhtml, but very useful for PDF and RTF. 98 * 99 * 'format' => the optional enumerating type : A, a, I, i, or 1 (default) 100 * as HTML <ol> tag's type attribute (only for number_... type) 101 * 102 * 'key' => the optional starting number/letter (not for items) 103 * 104 * @param array &$matches The array of matches from parse(). 105 * @return A delimited token to be used as a placeholder in 106 * the source text and containing the original block of text 107 * @access public 108 */ 109 function process(&$matches) 110 { 111 if (!empty($matches[3])) { 112 $this->_level++; 113 $expsub = preg_replace_callback( 114 $this->regex, 115 array(&$this, 'process'), 116 $matches[2] 117 ); 118 $this->_level--; 119 } else { 120 $expsub = $matches[2]; 121 } 122 if ($matches[1]) { 123 $this->_type[$this->_level] = 'number'; 124 if (is_numeric($matches[1])) { 125 $format = '1'; 126 $key = $matches[1] + 0; 127 } elseif (($matches[1] == 'i') || ($matches[1] == 'I')) { 128 $format = $matches[1]; 129 } else { 130 $format = 131 ($matches[1] >= 'a') && ($matches[1] <='z') ? 'a' : 'A'; 132 $key = $matches[1]; 133 } 134 } else { 135 $this->_type[$this->_level] = 'bullet'; 136 } 137 $this->_count[$this->_level] = -1; 138 $sub = preg_replace_callback( 139 $this->regexElement, 140 array(&$this, 'processElement'), 141 $expsub 142 ); 143 $param = array( 144 'level' => $this->_level, 145 'count' => $this->_count[$this->_level] ); 146 $param['type'] = $this->_type[$this->_level].'_list_start'; 147 if (isset($format)) { 148 $param['format'] = $format; 149 } 150 if (isset($key)) { 151 $param['key'] = $key; 152 } 153 $ret = $this->wiki->addToken($this->rule, $param ); 154 $param['type'] = $this->_type[$this->_level].'_list_end'; 155 return $ret . $sub . $this->wiki->addToken($this->rule, $param ); 156 } 157 158 /** 159 * Generates a replacement for the matched list elements. Token options are: 160 * 'type' => 161 * '[listType]_item_start' : the start of item text (bullet or number) 162 * '[listType]_item_end' : the end of item text (bullet or number) 163 * where [listType] is bullet or number 164 * 165 * 'level' => the indent level (0 for the first level, 1 for the 166 * second, etc) 167 * 168 * 'count' => the item ordeer at this level. 169 * 170 * @param array &$matches The array of matches from parse(). 171 * @return A delimited token to be used as a placeholder in 172 * the source text and containing the original block of text 173 * @access public 174 */ 175 function processElement(&$matches) 176 { 177 return $this->wiki->addToken($this->rule, array( 178 'type' => $this->_type[$this->_level] . '_item_start', 179 'level' => $this->_level, 180 'count' => ++$this->_count[$this->_level]) ) . 181 rtrim($matches[1]) . 182 $this->wiki->addToken($this->rule, array( 183 'type' => $this->_type[$this->_level] . '_item_end', 184 'level' => $this->_level, 185 'count' => $this->_count[$this->_level]) ); 186 } 187} 188