diff options
Diffstat (limited to 'includes/pear/Text/Wiki/Parse/Creole/List.php')
| -rw-r--r-- | includes/pear/Text/Wiki/Parse/Creole/List.php | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/includes/pear/Text/Wiki/Parse/Creole/List.php b/includes/pear/Text/Wiki/Parse/Creole/List.php new file mode 100644 index 0000000..36ab590 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/List.php @@ -0,0 +1,244 @@ +<?php + +/** + * + * Parses for bulleted and numbered lists. + * + * This class implements a Text_Wiki_Parse to find source text marked as + * a bulleted or numbered list. In short, if a line starts with '*' then + * it is a bullet list item; if a line starts with '#' then it is a + * number list item. Multiple * or # indicate an indented sub-list. + * The list items must be on sequential lines, and are ended by blank lines. + * Using a non-* non-# character at the beginning of a line ends the list. + * Note that single newline characters may be eaten beforehand by other rules. + * + * @category Text + * + * @package Text_Wiki + * + * @author Justin Patrin <papercrane@reversefold.com> + * @author Paul M. Jones <pmjones@php.net> + * @author Michele Tomaiuolo <tomamic@yahoo.it> + * + * @license LGPL + * + * @version $Id: List.php 240550 2007-08-01 07:57:34Z mic $ + * + */ + +class Text_Wiki_Parse_List extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\*[^\#\-\*]|\-[^\-\d\*\#]|\#[^\#\-\*]).*?)\n(?![\*\-#])/s'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => + * 'bullet_start' : the start of a bullet list + * 'bullet_end' : the end of a bullet list + * 'number_start' : the start of a number list + * 'number_end' : the end of a number list + * 'item_start' : the start of item text (bullet or number) + * 'item_end' : the end of item text (bullet or number) + * 'unknown' : unknown type of list or item + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * 'count' => the list item number at this level. not needed for + * xhtml, but very useful for PDF and RTF. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return + $return = ''; + + // the list of post-processing matches + $list = array(); + + // a stack of list-start and list-end types; we keep this + // so that we know what kind of list we're working with + // (bullet or number) and what indent level we're at. + $stack = array(); + + // the item count is the number of list items for any + // given list-type on the stack + $itemcount = array(); + + // have we processed the very first list item? + $pastFirst = false; + + // populate $list with this set of matches. $matches[1] is the + // text matched as a list set by parse(). + preg_match_all( + '/^((\*|\-|#)+) *(.*?)$/ms', + $matches[1], + $list, + PREG_SET_ORDER + ); + + if (count($list) === 1 && $matches[0][0] === '*' && $matches[0][1] !== ' ' && strpos($matches[0], '*', 1)) { + return $matches[0]; + } + + // loop through each list-item element. + foreach ($list as $key => $val) { + // $val[0] is the full matched list-item line + // $val[1] is the level (number) + // $val[2] is the type (* or #) + // $val[3] is the list item text + + // how many levels are we indented? (1 means the "root" + // list level, no indenting.) + $stars = $val[1]; + $level = strlen($stars); + $last = $stars[strlen($stars) - 1]; + + // get the list item type + if ($last == '*' || $last == '-') { + $type = 'bullet'; + } elseif ($last == '#') { + $type = 'number'; + } else { + $type = 'unknown'; + } + + // get the text of the list item + $text = $val[3]; + + // remove a level from the list? + while (count($stack) > $level || (count($stack) == $level && $type != $stack[$level - 1])) { + + // so we don't keep counting the stack, we set up a temp + // var for the count. -1 becuase we're going to pop the + // stack in the next command. $tmp will then equal the + // current level of indent. + $tmp = count($stack) - 1; + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => $tmp + ) + ); + + // reset to the current (previous) list type so that + // the new list item matches the proper list type. + if ($tmp) { + $oldtype = $stack[$tmp - 1]; + } + + // reset the item count for the popped indent level + unset($itemcount[$tmp + 1]); + } + + // add a level to the list? + if ($level > count($stack)) { + + // the current indent level is greater than the + // number of stack elements, so we must be starting + // a new list. push the new list type onto the + // stack... + array_push($stack, $type); + + // ...and add a list-start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_list_start', + 'level' => $level - 1 + ) + ); + } + + // add to the item count for this list (taking into account + // which level we are at). + if (! isset($itemcount[$level])) { + // first count + $itemcount[$level] = 0; + } else { + // increment count + $itemcount[$level]++; + } + + // is this the very first item in the list? + if (! $pastFirst) { + $first = true; + $pastFirst = true; + } else { + $first = false; + } + + // create a list-item starting token. + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_start', + 'level' => $level, + 'count' => $itemcount[$level], + 'first' => $first + ) + ); + + // create a list-item ending token. + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_end', + 'level' => $level, + 'count' => $itemcount[$level] + ) + ); + + // add the starting token, list-item text, and ending token + // to the return. + $return .= "\n" . $start . $text . $end; + } + + // the last list-item may have been indented. go through the + // list-type stack and create end-list tokens until the stack + // is empty. + while (count($stack) > 0) { + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => count($stack) + ) + ); + } + + // we're done! send back the replacement text. + return "\n\n" . $return . "\n\n"; + } +} +?>
\ No newline at end of file |
