5 * Parses for bulleted and numbered lists.
11 * @author Paul M. Jones <pmjones@php.net>
15 * @version $Id: List.php,v 1.3 2005/02/23 17:38:29 pmjones Exp $
21 * Parses for bulleted and numbered lists.
23 * This class implements a Text_Wiki_Parse to find source text marked as
24 * a bulleted or numbered list. In short, if a line starts with '* ' then
25 * it is a bullet list item; if a line starts with '# ' then it is a
26 * number list item. Spaces in front of the * or # indicate an indented
27 * sub-list. The list items must be on sequential lines, and may be
28 * separated by blank lines to improve readability. Using a non-* non-#
29 * non-whitespace character at the beginning of a line ends the list.
35 * @author Paul M. Jones <pmjones@php.net>
39 class Text_Wiki_Parse_List
extends Text_Wiki_Parse
{
44 * The regular expression used to parse the source text and find
45 * matches conforming to this rule. Used by the parse() method.
55 var $regex = '/\n((\*|#) .*\n)(?! {0,}(\* |# |\n))/Us';
60 * Generates a replacement for the matched text. Token options are:
63 * 'bullet_start' : the start of a bullet list
64 * 'bullet_end' : the end of a bullet list
65 * 'number_start' : the start of a number list
66 * 'number_end' : the end of a number list
67 * 'item_start' : the start of item text (bullet or number)
68 * 'item_end' : the end of item text (bullet or number)
69 * 'unknown' : unknown type of list or item
71 * 'level' => the indent level (0 for the first level, 1 for the
74 * 'count' => the list item number at this level. not needed for
75 * xhtml, but very useful for PDF and RTF.
79 * @param array &$matches The array of matches from parse().
81 * @return A series of text and delimited tokens marking the different
82 * list text and list elements.
86 function process(&$matches)
88 // the replacement text we will return
91 // the list of post-processing matches
94 // a stack of list-start and list-end types; we keep this
95 // so that we know what kind of list we're working with
96 // (bullet or number) and what indent level we're at.
99 // the item count is the number of list items for any
100 // given list-type on the stack
101 $itemcount = array();
103 // have we processed the very first list item?
106 // populate $list with this set of matches. $matches[1] is the
107 // text matched as a list set by parse().
109 '=^( {0,})(\*|#) (.*)$=Ums',
115 // loop through each list-item element.
116 foreach ($list as $key => $val) {
118 // $val[0] is the full matched list-item line
119 // $val[1] is the number of initial spaces (indent level)
120 // $val[2] is the list item type (* or #)
121 // $val[3] is the list item text
123 // how many levels are we indented? (1 means the "root"
124 // list level, no indenting.)
125 $level = strlen($val[1]) +
1;
127 // get the list item type
128 if ($val[2] == '*') {
130 } elseif ($val[2] == '#') {
136 // get the text of the list item
139 // add a level to the list?
140 if ($level > count($stack)) {
142 // the current indent level is greater than the
143 // number of stack elements, so we must be starting
144 // a new list. push the new list type onto the
146 array_push($stack, $type);
148 // ...and add a list-start token to the return.
149 $return .= $this->wiki
->addToken(
152 'type' => $type . '_list_start',
153 'level' => $level - 1
158 // remove a level from the list?
159 while (count($stack) > $level) {
161 // so we don't keep counting the stack, we set up a temp
162 // var for the count. -1 becuase we're going to pop the
163 // stack in the next command. $tmp will then equal the
164 // current level of indent.
165 $tmp = count($stack) - 1;
167 // as long as the stack count is greater than the
168 // current indent level, we need to end list types.
169 // continue adding end-list tokens until the stack count
170 // and the indent level are the same.
171 $return .= $this->wiki
->addToken(
174 'type' => array_pop($stack) . '_list_end',
179 // reset to the current (previous) list type so that
180 // the new list item matches the proper list type.
181 $type = $stack[$tmp - 1];
183 // reset the item count for the popped indent level
184 unset($itemcount[$tmp +
1]);
187 // add to the item count for this list (taking into account
188 // which level we are at).
189 if (! isset($itemcount[$level])) {
191 $itemcount[$level] = 0;
194 $itemcount[$level]++
;
197 // is this the very first item in the list?
205 // create a list-item starting token.
206 $start = $this->wiki
->addToken(
209 'type' => $type . '_item_start',
211 'count' => $itemcount[$level],
216 // create a list-item ending token.
217 $end = $this->wiki
->addToken(
220 'type' => $type . '_item_end',
222 'count' => $itemcount[$level]
226 // add the starting token, list-item text, and ending token
228 $return .= $start . $val[3] . $end;
231 // the last list-item may have been indented. go through the
232 // list-type stack and create end-list tokens until the stack
234 while (count($stack) > 0) {
235 $return .= $this->wiki
->addToken(
238 'type' => array_pop($stack) . '_list_end',
239 'level' => count($stack)
244 // we're done! send back the replacement text.
245 return "\n" . $return . "\n\n";