Subversion Repositories Applications.papyrus

Rev

Rev 1688 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
248 jpm 1
<?php
2
// $Id: List.php,v 1.1 2005-01-20 19:43:20 jpm Exp $
3
 
4
 
5
/**
6
*
7
* This class implements a Text_Wiki_Parse to find source text marked as
8
* a bulleted or numbered list.  In short, if a line starts with '* ' then
9
* it is a bullet list item; if a line starts with '# ' then it is a
10
* number list item.  Spaces in front of the * or # indicate an indented
11
* sub-list.  The list items must be on sequential lines, and may be
12
* separated by blank lines to improve readability.  Using a non-* non-#
13
* non-whitespace character at the beginning of a line ends the list.
14
*
15
* @author Paul M. Jones <pmjones@ciaweb.net>
16
*
17
* @package Text_Wiki
18
*
19
*/
20
 
21
class Text_Wiki_Parse_List extends Text_Wiki_Parse {
22
 
23
 
24
    /**
25
    *
26
    * The regular expression used to parse the source text and find
27
    * matches conforming to this rule.  Used by the parse() method.
28
    *
29
    * @access public
30
    *
31
    * @var string
32
    *
33
    * @see parse()
34
    *
35
    */
36
 
37
    var $regex = '/\n((\*|#) .*\n)(?! {0,}(\* |# |\n))/Us';
38
 
39
 
40
    /**
41
    *
42
    * Generates a replacement for the matched text.  Token options are:
43
    *
44
    * 'type' =>
45
    *     'bullet_start' : the start of a bullet list
46
    *     'bullet_end'   : the end of a bullet list
47
    *     'number_start' : the start of a number list
48
    *     'number_end'   : the end of a number list
49
    *     'item_start'   : the start of item text (bullet or number)
50
    *     'item_end'     : the end of item text (bullet or number)
51
    *     'unknown'      : unknown type of list or item
52
    *
53
    * 'level' => the indent level (0 for the first level, 1 for the
54
    * second, etc)
55
    *
56
    * 'count' => the list item number at this level. not needed for
57
    * xhtml, but very useful for PDF and RTF.
58
    *
59
    * @access public
60
    *
61
    * @param array &$matches The array of matches from parse().
62
    *
63
    * @return A series of text and delimited tokens marking the different
64
    * list text and list elements.
65
    *
66
    */
67
 
68
    function process(&$matches)
69
    {
70
        // the replacement text we will return
71
        $return = '';
72
 
73
        // the list of post-processing matches
74
        $list = array();
75
 
76
        // a stack of list-start and list-end types; we keep this
77
        // so that we know what kind of list we're working with
78
        // (bullet or number) and what indent level we're at.
79
        $stack = array();
80
 
81
        // the item count is the number of list items for any
82
        // given list-type on the stack
83
        $itemcount = array();
84
 
85
        // have we processed the very first list item?
86
        $pastFirst = false;
87
 
88
        // populate $list with this set of matches. $matches[1] is the
89
        // text matched as a list set by parse().
90
        preg_match_all(
91
            '=^( {0,})(\*|#) (.*)$=Ums',
92
            $matches[1],
93
            $list,
94
            PREG_SET_ORDER
95
        );
96
 
97
        // loop through each list-item element.
98
        foreach ($list as $key => $val) {
99
 
100
            // $val[0] is the full matched list-item line
101
            // $val[1] is the number of initial spaces (indent level)
102
            // $val[2] is the list item type (* or #)
103
            // $val[3] is the list item text
104
 
105
            // how many levels are we indented? (1 means the "root"
106
            // list level, no indenting.)
107
            $level = strlen($val[1]) + 1;
108
 
109
            // get the list item type
110
            if ($val[2] == '*') {
111
                $type = 'bullet';
112
            } elseif ($val[2] == '#') {
113
                $type = 'number';
114
            } else {
115
                $type = 'unknown';
116
            }
117
 
118
            // get the text of the list item
119
            $text = $val[3];
120
 
121
            // add a level to the list?
122
            if ($level > count($stack)) {
123
 
124
                // the current indent level is greater than the
125
                // number of stack elements, so we must be starting
126
                // a new list.  push the new list type onto the
127
                // stack...
128
                array_push($stack, $type);
129
 
130
                // ...and add a list-start token to the return.
131
                $return .= $this->wiki->addToken(
132
                    $this->rule,
133
                    array(
134
                        'type' => $type . '_list_start',
135
                        'level' => $level - 1
136
                    )
137
                );
138
            }
139
 
140
            // remove a level from the list?
141
            while (count($stack) > $level) {
142
 
143
                // so we don't keep counting the stack, we set up a temp
144
                // var for the count.  -1 becuase we're going to pop the
145
                // stack in the next command.  $tmp will then equal the
146
                // current level of indent.
147
                $tmp = count($stack) - 1;
148
 
149
                // as long as the stack count is greater than the
150
                // current indent level, we need to end list types.
151
                // continue adding end-list tokens until the stack count
152
                // and the indent level are the same.
153
                $return .= $this->wiki->addToken(
154
                    $this->rule,
155
                    array (
156
                        'type' => array_pop($stack) . '_list_end',
157
                        'level' => $tmp
158
                    )
159
                );
160
 
161
                // reset to the current (previous) list type so that
162
                // the new list item matches the proper list type.
163
                $type = $stack[$tmp - 1];
164
 
165
                // reset the item count for the popped indent level
166
                unset($itemcount[$tmp + 1]);
167
            }
168
 
169
            // add to the item count for this list (taking into account
170
            // which level we are at).
171
            if (! isset($itemcount[$level])) {
172
                // first count
173
                $itemcount[$level] = 0;
174
            } else {
175
                // increment count
176
                $itemcount[$level]++;
177
            }
178
 
179
            // is this the very first item in the list?
180
            if (! $pastFirst) {
181
                $first = true;
182
                $pastFirst = true;
183
            } else {
184
                $first = false;
185
            }
186
 
187
            // create a list-item starting token.
188
            $start = $this->wiki->addToken(
189
                $this->rule,
190
                array(
191
                    'type' => $type . '_item_start',
192
                    'level' => $level,
193
                    'count' => $itemcount[$level],
194
                    'first' => $first
195
                )
196
            );
197
 
198
            // create a list-item ending token.
199
            $end = $this->wiki->addToken(
200
                $this->rule,
201
                array(
202
                    'type' => $type . '_item_end',
203
                    'level' => $level,
204
                    'count' => $itemcount[$level]
205
                )
206
            );
207
 
208
            // add the starting token, list-item text, and ending token
209
            // to the return.
210
            $return .= $start . $val[3] . $end;
211
        }
212
 
213
        // the last list-item may have been indented.  go through the
214
        // list-type stack and create end-list tokens until the stack
215
        // is empty.
216
        while (count($stack) > 0) {
217
            $return .= $this->wiki->addToken(
218
                $this->rule,
219
                array (
220
                    'type' => array_pop($stack) . '_list_end',
221
                    'level' => count($stack)
222
                )
223
            );
224
        }
225
 
226
        // we're done!  send back the replacement text.
227
        return "\n" . $return . "\n\n";
228
    }
229
}
230
?>