includes/wikiengine/Parse/Mediawiki/Deflist.php
changeset 1 fe660c52c48f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/wikiengine/Parse/Mediawiki/Deflist.php	Wed Jun 13 16:07:17 2007 -0400
@@ -0,0 +1,277 @@
+<?php
+// vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
+/**
+* 
+* Mediawiki: Parse for definition lists.
+* 
+* @category Text
+* @package Text_Wiki
+* @author Justin Patrin <papercrane@reversefold.com>
+* @author Paul M. Jones <pmjones@php.net>
+* @author Moritz Venn <ritzmo@php.net>
+* @license LGPL
+* @version $Id: Deflist.php,v 1.1 2006/03/29 18:41:43 ritzmo Exp $
+* 
+*/
+
+/**
+* 
+* Parses for definition lists.
+*
+* This class implements a Text_Wiki_Parse to find source text marked as a
+* definition list.
+* If a line starts with ';' or ':' it is considered a part of a definition
+* list. ';' indicates the term to be defined and ':' indicates its definition.
+* As in Mediawiki we also allow definition lists to only consist of one
+* item-type.
+*
+* @category Text
+* @package Text_Wiki
+* 
+* @author Justin Patrin <papercrane@reversefold.com>
+* @author Paul M. Jones <pmjones@php.net>
+* @author Moritz Venn <ritzmo@php.net>
+* 
+*/
+
+class Text_Wiki_Parse_Deflist 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((?:\;|\:)+.*?\n(?!(?:\;|\:)+))/s';
+ 
+   /**
+    *
+    * Generates a replacement for the matched text.  Token options are:
+    *
+    * 'type' =>
+    *     'list_start'    : the start of a definition list
+    *     'list_end'      : the end of a definition list
+    *     'term_start'    : the start of a definition term
+    *     'term_end'      : the end of a definition term
+    *     'narr_start'    : the start of definition narrative
+    *     'narr_end'      : the end of definition narrative
+    *     'unknown'       : unknown type of definition portion
+    *
+    * '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
+        );
+
+	// loop through each list-item element.
+        foreach ($list as $key => $val) {
+            // $val[0] is the full matched list-item line
+            // $val[1] is the type (* or #)
+            // $val[2] is the level (number)
+            // $val[3] is the list item text
+            
+            // how many levels are we indented? (1 means the "root"
+            // list level, no indenting.)
+            $level = strlen($val[1]);
+            
+            // get the list item type
+            if ($val[2] == ';') {
+                $type = 'term';
+            } elseif ($val[2] == ':') {
+                $type = 'narr';
+            } else {
+                $type = 'unknown';
+            }
+            
+            // get the text of the list item
+            $text = $val[3];
+
+            // 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);
+
+		// The new list has to be opened in an item (use current type)
+		if ($level > 1) {
+		$return .= $this->wiki->addToken(
+		    $this->rule,
+		    array(
+		        'type' => $type . '_start',
+		        'level' => $level - 1
+                    )
+                );
+		}
+		// ...and add a list-start token to the return.
+                $return .= $this->wiki->addToken(
+                    $this->rule, 
+                    array(
+                        'type' => 'list_start',
+                        'level' => $level - 1
+                    )
+                );
+            }
+
+	    // remove a level from the list?
+	    while (count($stack) > $level) {
+                // 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' => 'list_end',
+                        'level' => $tmp
+                    )
+                );
+
+                array_pop($stack);
+
+		// reset to the current (previous) list type so that
+                // the new list item matches the proper list type.
+		$type = $stack[$tmp - 1];
+
+		// Close the previously opened List item
+		$return .= $this->wiki->addToken(
+                    $this->rule,
+                    array (
+                        'type' => $type . '_end',
+                        'level' => $tmp
+                    )
+                );
+                
+                // reset the item count for the popped indent level
+                unset($itemcount[$tmp + 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 . '_start',
+                    'level' => $level,
+                    'count' => $itemcount[$level],
+                    'first' => $first
+                )
+            );
+            
+            // create a list-item ending token.
+            $end = $this->wiki->addToken(
+                $this->rule, 
+                array(
+                    'type' => $type . '_end',
+                    'level' => $level,
+                    'count' => $itemcount[$level]
+                )
+            );
+            
+            // add the starting token, list-item text, and ending token
+            // to the return.
+            $return .= $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.
+	$level = count($stack);
+	while ($level > 0) {
+	    array_pop($stack);
+            $return .= $this->wiki->addToken(
+                $this->rule, 
+                array (
+                    'type' => 'list_end',
+                    'level' => $level - 1
+                )
+            );
+
+            // if we are higher than level 1 we need to close fake items
+            if ($level > 1) {
+		$return .= $this->wiki->addToken(
+                $this->rule,
+                array (
+                    'type' => $stack[$level - 2] . '_end',
+                    'level' => $level - 2
+                )
+                );
+	    }
+	    $level = count($stack);
+	}
+        
+        // we're done!  send back the replacement text.
+        return "\n" . $return . "\n\n";
+    }
+}
+?>