Simplewiki 1

muster_simplewiki_parser.php

Go to the documentation of this file.
00001 <?php
00009 #==========================================================================
00010 #-----------------------------[ PARSER ]----------------------------------
00011 #==========================================================================
00012 
00026 class Muster_SimpleWiki_Parser
00027 {
00038         protected $link_inline_re; 
00039         protected $image_inline_re; 
00040         protected $item_inline_re; 
00041         protected $defitem_inline_re; 
00042         protected $cell_re; 
00043         protected $decorator_re; 
00044         protected $tablerow_setaside_re; 
00045         protected $pre_escape_re; 
00046         protected $_rules; 
00047         protected $block_re; 
00048         protected $inline_re; 
00049 
00052 
00053         protected $_curnode;
00055         protected $_leaftextnode;
00057         protected $_root;
00060 
00061         protected $_raw;
00063         protected $_preprocessed_raw;
00065         protected $_argchars = '^()'; // no parenthesis
00066 //      protected $_argchars = '\\w\\s:="\'%\\\#.-'; // for decorators, notably parentheses omitted for security
00068         protected $_metadata; // from first line ```## arguments
00070         protected $_markerdata; // from preprocessing
00074         protected $_markers;
00082         public function __construct($text)
00083         {
00084                 $this->_set_rules();
00085                 $this->_set_re($this->_rules);
00086                 $this->prepare($text);
00087         }
00099         protected function _set_rules()
00100         {
00101                 // the first group name of each rule, if set, is used by controller (_create_node($preg_groups)) 
00102                 // for further processing of parsed data
00103                 $rules = new StdClass();
00104                 $argchars = $this->_argchars;
00105                 #================================[ basic processing ]=================================#
00106                 # no explicit action by user (other than include blank lines between blocks)
00107 //              $rules->char =  '(?P<char> . )'; // slower, but allows capture of raw url's
00108 //              $rules->char =  '(?P<char> ([\\w\\s]+$|. ))'; //faster, but misses raw url's, twice as slow as markup filter
00109                 // markup characters: =/*-\[]|#{}%:<>~
00112                 $rules->char =  '(?P<char> ([^\/\*\\\[{%<~]+|.))'; // characters before next inline markup start, or markup start char
00114                 $rules->blankline = '(?P<blankline> ^ \s* $ )'; 
00116                 $rules->paragraph = '(?P<paragraph>
00117                         ^\s*(\|:p\s+(?P<paragraph_decorator>['.$argchars.']+?):\|)?(?P<text_chars>.+)
00118                         |
00119                         (?P<text_charstream>.+) 
00120                 )'; 
00121                 #================================[ core markup ]===============================#
00122                 #--------------------------------[ basic markup ]------------------------------#
00123 
00124                 $rules->heading = '(?P<heading>
00125             ^\s*(\|:h\s+(?P<heading_decorator>['.$argchars.']+?):\|)? \s*
00126             (?P<heading_head>={1,6}) \s*
00127             (?P<heading_text> .*? ) \s*
00128             (?P<heading_tail>=*) \s*
00129             $
00130         )';
00135                 $rules->emph = '(?P<emph> (?<!~)\/\/ )'; 
00137                 $rules->strong = '(?P<strong> \*\* )';
00139                 $rules->linebreak = '(?P<linebreak> \\\\\\\\ )';
00140                 // \b horizontalrule: horizontal rule: "-----*"
00141                 $rules->horizontalrule = '(?P<horizontalrule>
00142                         ^ \s* -----* \s* $ 
00143                 )';
00144                 #--------------------------------[ links ]-------------------------------------#
00145 
00146                 $rules->link = '(?P<link>
00147             (%l\s+(?P<link_decorator>['.$argchars.']+?)%)?
00148                         \[\[
00149             (?P<link_target>\S+?) \s*
00150             (\| \s* (?P<link_text>.*?) \s* (\| \s* (?P<link_title>[^|\]}]+))? \s*)?
00151             \]\](?!]) # allow embedded "]"
00152                 )';
00153                 #--------------------------------[ images ]-------------------------------------#
00154 
00155                 $rules->image = '(?P<image>
00156             (%i\s+(?P<image_decorator>['.$argchars.']+?)%)?
00157                         {{
00158             (?P<image_target>\S+?) \s*
00159             (\| \s* (?P<image_text>.*?) \s* (\| \s* (?P<image_title>[^|\]}]+))? \s*)?
00160             }}
00161                 )';
00162                 #--------------------------------[ lists ]-------------------------------------#
00163 
00164 
00165                 $rules->deflist = '(?P<deflist>
00166             ^ \s*
00167                         (\|:d[ltd]\s+(['.$argchars.']+?):\|){0,3}
00168                         (:(?=[^:])).* $ # only one opening list marker allowed
00169             (\n [\t\x20]*
00170                         (\|:d[ltd]\s+(['.$argchars.']+?):\|){0,3}
00171                         :+.* $ 
00172                         )*
00173         )'; 
00175                 $rules->defitem = '(?P<defitem>
00176             ^\s*
00177                         (\|:dl\s+(?P<deflist_decorator>(['.$argchars.']+?)):\|)?
00178                         (\|:dt\s+(?P<defterm_decorator>(['.$argchars.']+?)):\|)?
00179                         (\|:dd\s+(?P<defdesc_decorator>(['.$argchars.']+?)):\|)?
00180             (?P<defitem_head> :+) \s*
00181             ((?P<defterm_text> .*?)(?<!~)::)?
00182             (?P<defdesc_text> .*?)
00183             \s*$
00184         )';
00187                 $rules->list = '(?P<list>
00188             ^\s*
00189                         (\|:([uo]l|li) \s+(['.$argchars.']+?):\|){0,2}
00190                         ([*\\#](?=[^*\\#])).* $ # only one opening list marker allowed
00191             (\n [\t\x20]*
00192                         (\|:([uo]l|li) \s+(['.$argchars.']+?):\|){0,2}
00193                         [*\#]+.* $ 
00194                         )*
00195         )'; 
00197                 $rules->item = '(?P<item>
00198             ^\s*
00199                         (\|:[uo]l\s+(?P<list_decorator> (['.$argchars.']+?)):\|)?
00200                         (\|:li\s+(?P<item_decorator>(['.$argchars.']+?)):\|)?
00201             (?P<item_head> [\#*]+) \s*
00202             (?P<item_text> .*)
00203             \s*$
00204         )';
00205                 #--------------------------------[ tables ]-------------------------------------#
00206 
00207                 $rules->table = '(?P<table>
00208             ^\s*
00209                         (\|:table\\s+(?P<table_decorator>(['.$argchars.']+?)):\|)?
00210                         (\|:tr\s+(?P<row_decorator>(['.$argchars.']+?)):\|)? 
00211                         \s*
00212                         (?P<table_row>
00213             (((?<!~)\|:t[dh]\s+(['.$argchars.']+?):\|)?\|(?!:[a-z]).*?)* \s*
00214             \| \s*
00215                         )
00216             \s*$
00217         )';
00219                 $rules->cell = '
00220             (\|:t[dh]\s+(?P<cell_decorator>['.$argchars.']+?):\|)?
00221                         \| \s*
00222             (
00223                 (?P<head> = ([^|]|(?<=~)\|)+ ) |
00224                 (?P<cell> ([^|]|(?<=~)\|)+ )
00225             ) \s* 
00226         ';
00227                 #================================[ escape character ]=================================#
00228 
00229                 $rules->escape = '(?P<escape> ~ (?P<escaped_char>\S) )'; # embedded in various regex's
00230                 #================================[ special decorators ]===============================#
00231                 #--------------------------------[ span decoration ]----------------------------------#
00233                 $rules->span = '(?P<span> %(s\s+(?P<span_decorator>['.$argchars.']+?))?% )';
00234                 #--------------------------------[ block dividers ]-----------------------------------#
00236                 $rules->blockdivider = '(?P<blockdivider>
00237                         ^\s* \|:b \s+(?P<blockdivider_decorator>(['.$argchars.']+?)):\| \s* $ 
00238                 )'; # generic block
00239                 #===============================[ preformatted text ]=================================#
00240                 // inline
00242                 $rules->code = '(?P<code>
00243                         (%c\s+(?P<code_decorator>['.$argchars.']+?)%)?{{{ (?P<code_text>.*?) }}} 
00244                 )';
00246                 $rules->pre = '(?P<pre>
00247             ^\s*(\|:pre\s+(?P<pre_decorator>['.$argchars.']+?):\|)?(?<!~){{{ \s* $
00248             (\n)?
00249             (?P<pre_markup>
00250                 ([\#]!(?P<pre_type>\w*?)(\s+.*)?$)?
00251                 (.|\n)+?
00252             )
00253             \n?
00254             ^\s*}}} \s*$
00255         )';
00257                 $rules->pre_escape = ' ^(?P<indent>\s*) ~ (?P<rest> \}\}\} \s*) $';
00258                 #================================[ advanced markup ]===============================#
00259                 #--------------------------------[ block declarations ]------------------------------#
00262                 $rules->blockdef = '
00263                         ^(?P<blockdef>
00264                         \n?(?P<block_indent>[\t\x20]*)\(:(?P<block_selector>\w+)(\s+(?P<block_decorator>['.$argchars.']+?))? \s* :\)
00265                         \s*?(?P<block_inline>.*) $
00266                         (?P<block_content>(\n.*$)*?)
00267                         \n(?P=block_indent)\(:(?P=block_selector)end\s*:\)\s*$
00268                 )';
00269 
00270                 #--------------------------------[ macros ]--------------------------------#
00272                 $rules->macro = '(?P<macro>
00273                         <<
00274             (?P<macro_name> \w+)
00275             ((?P<macro_args> ['.$argchars.']*) )? \s*
00276             (\| \s* (?P<macro_text> .+?) \s* )?
00277             >>
00278         )'; 
00280                 $rules->blockmacro = '(?P<blockmacro>
00281                         ^ \s*
00282                         <<
00283             (?P<blockmacro_name> \w+)
00284             ((?P<blockmacro_args> ['.$argchars.']*) )? \s*
00285             (\| \s* (?P<blockmacro_text> .+?) \s* )?
00286             >> \s*
00287                         $
00288         )';
00290                 $rules->decorator = '
00291                         (?>(?P<variable>[\w-]+)(?P<operator>[:=]))?     # optional attribute or property name, and operator applied
00292                         (
00293                                 "(?P<ddelim_value>.*?)(?<!\\\)"                         # double quote delimited
00294                         |
00295                                 \'(?P<sdelim_value>.*?)(?<!\\\)\'                       # single quote delimited
00296                         |
00297                                 (?P<ndelim_value>\S+)                                           # not delimited
00298                         )
00299                 ';
00300                 $this->_rules = $rules;
00301         }
00302         #---------------------------------------------------------------------------------------#
00303         #------------------------------[ set regular expressions ]------------------------------#
00304         #---------------------------------------------------------------------------------------#
00305 
00318         protected function _set_re($rules)
00319         {
00320                 // from least to most general
00321                 // For special case pre escaping, in creole 1.0 done with ~:
00322                 $this->pre_escape_re = '/' . $rules->pre_escape . '/xm';
00323                 // For sub-processing: includes image, but excludes links
00324                 $this->link_inline_re = "/\n"
00325                         . implode("\n|\n",
00326                         array($rules->code, $rules->image, $rules->strong, 
00327                                 $rules->emph, $rules->span, $rules->linebreak, 
00328                                 $rules->escape, $rules->char))
00329                         . "\n/x"; # for link captions
00330                 // For sub-processing: includes links, but excludes images
00331                 $this->image_inline_re = "/\n"
00332                         . implode("\n|\n",
00333                                 array($rules->link, $rules->code, $rules->strong, 
00334                                 $rules->emph, $rules->span, $rules->linebreak, 
00335                                 $rules->escape, $rules->char))
00336                         . "\n/x"; # for image captions
00337                 $this->item_inline_re = '/' . $rules->item . '/xm'; // for list items
00338                 $this->defitem_inline_re = '/' . $rules->defitem . '/xm'; // for def list items
00339                 $this->cell_re = '/' . $rules->cell . '/x'; // for quick table cells
00340                 // For inline elements:
00341                 $this->inline_re = "/\n" 
00342                         . implode("\n|\n", 
00343                                 array($rules->link, $rules->macro,
00344                                 $rules->code, $rules->image, $rules->strong, $rules->emph, 
00345                                 $rules->span, $rules->linebreak, $rules->escape, $rules->char))
00346                         . "\n/x";
00347                 // set aside table row contents
00348                 $this->tablerow_setaside_re =  "/\n" 
00349                         . implode("\n|\n", array($rules->link, $rules->macro,$rules->code,$rules->image))
00350                         . "\n/x";
00351                 // For block elements:
00352                 $this->block_re = "/\n" 
00353                         . implode("\n|\n",
00354                                 array($rules->blankline, $rules->blockdef, $rules->heading, 
00355                                 $rules->horizontalrule, $rules->blockdivider, $rules->blockmacro,
00356                                 $rules->pre, $rules->list, $rules->deflist, $rules->table, $rules->paragraph)) 
00357                         . "\n/xm";
00358                 $this->decorator_re = '/' . $rules->decorator . '/x';
00359         }
00369         public function metadata()
00370         {
00371                 return $this->_metadata;
00372         }
00380         public function markerdata()
00381         {
00382                 return $this->_markerdata;
00383         }
00389         public function preprocessed_markup()
00390         {
00391                 return $this->_preprocessed_markup;
00392         }
00397         public function argchars($argchars = NULL)
00398         {
00399                 if (!is_null($argchars))
00400                 {
00401                         $this->_argchars = $argchars;
00402                         // recompile regex
00403                         $this->_set_rules();
00404                         $this->_set_re($this->_rules);
00405                 }
00406                 return $this->_argchars;
00407         }
00409         #---------------------[ process initiation ]--------------------------#
00410 
00424         public function prepare($markup)
00425         {
00426                 $this->_raw = $markup;
00427         $this->_root = new SimpleWiki_DocNode(SimpleWiki_DocNode::DOCUMENT); # 'document' is the top level node
00428         $this->_curnode = $this->_root;    # The most recent document node
00429         $this->_leaftextnode = NULL;           # The node to add inline characters to
00430                 $raw = $this->preprocess_raw_markup($this->_raw);
00431                 return $this;
00432         }
00439     public function parse() // initiate parsing
00440         {
00441                 # try to clean $raw of unnecessary newlines
00442         # parse the text given as $this->_raw...
00443         $this->_parse_block($this->_preprocessed_raw);
00444                 #...and return DOM tree.
00445         return $this->_root;
00446         }
00456     protected function _parse_block($raw)
00457         {
00458         # Recognize block elements.
00459         preg_replace_callback($this->block_re, array($this,'_create_node'), $raw);
00460         }
00470     protected function _parse_inline($raw)
00471         {
00472         # Recognize inline elements inside blocks.
00473         preg_replace_callback($this->inline_re, array($this,'_create_node'), $raw);
00474         }
00475         #---------------------[ process control ]--------------------------#
00476 
00486     protected function _create_node($preg_groups) // controller
00487         {
00488         # Invoke appropriate _*_node method. Called for every matched group.
00489                 foreach ($preg_groups as $name => $text)
00490                 {
00491                         if ((!is_int($name)) and ($text != ''))
00492                         {
00493                                 $node_method = "_{$name}_node";
00494                                 $this->$node_method($preg_groups);
00495                                 return;
00496                         }
00497                 }
00498                 # special case: pick up empty line for block boundary
00499                 $keys = array_keys($preg_groups);
00500                 $name = 'blankline';
00501                 if ($keys[count($keys)-2]==$name) // last name in key array indicates returned as found
00502                 {
00503                         $node_method = "_{$name}_node";
00504                         $this->$node_method($preg_groups);
00505                         return;
00506                 }
00507         }
00510 
00511         protected $_pre_markers = array();
00513         protected $_pre_markup = array();
00515         protected $_pre_count = 0;
00523         protected function add_pre_and_code_markers($preg_groups)
00524         {
00525                 isset($preg_groups['pre']) or ($preg_groups['pre'] = '');
00526                 isset($preg_groups['code']) or ($preg_groups['code'] = '');
00527                 $this->_pre_markup[] = preg_replace('/(\$|\\\\)(?=\d)/', '\\\\\1', // escape backreference markup
00528                         $preg_groups['pre'].$preg_groups['code']); // one or the other
00529                 $this->_pre_count++;
00530                 $marker = '{{{' . chr(255). $this->_pre_count . '}}}';
00531                 $this->_pre_markers[] = '/{{\\{' . chr(255) . $this->_pre_count . '\\}}}/';
00532                 return $marker;
00533         }
00535         protected $_tablerow_markers = array();
00537         protected $_tablerow_markup = array();
00539         protected $_tablerow_count = 0;
00546         protected function add_tablerow_markers($preg_groups)
00547         {
00548                 isset($preg_groups['link']) or ($preg_groups['link'] = '');
00549                 isset($preg_groups['macro']) or ($preg_groups['macro'] = '');
00550                 isset($preg_groups['code']) or ($preg_groups['code'] = '');
00551                 isset($preg_groups['image']) or ($preg_groups['image'] = '');
00552                 ($value = $preg_groups['link']) or ($value = $preg_groups['macro']) or 
00553                         ($value = $preg_groups['code']) or ($value = $preg_groups['image']);
00554                 $this->_tablerow_markup[] = $value;
00555                 $this->_tablerow_count++;
00556                 $marker = '{{{' . chr(255). $this->_tablerow_count . '}}}';
00557                 $this->_tablerow_markers[] = '/{{\\{' . chr(255) . $this->_tablerow_count . '\\}}}/';
00558                 return $marker;
00559         }
00572         protected function preprocess_raw_markup($raw)
00573         {
00574                 # get metadata
00575                 $raw = preg_replace_callback('/\A```##(.*$(\n``.*$)*)/m',array($this,'preprocess_metadata'),$raw);
00576                 $raw = "\n".$raw."\n"; // in case there is comment on first line, lookahead on last
00577                 # remove comments
00578                 $raw = preg_replace('/\n```.*$/m','',$raw);
00579                 # remove line continuations
00580                 $raw = preg_replace('/\n``/','',$raw);
00581                 # set aside preformatted blocks
00582                 $raw = preg_replace_callback('/'.$this->_rules->pre .'|' .$this->_rules->code .'/xm',array($this,'add_pre_and_code_markers'),$raw);
00583                 # add markup around raw url's; this allows "//" emphasis markup to operate without constraint
00584                 $raw = preg_replace('/(^|\W)((?<!\[\[|{{|~)(http[s]?|mailto):\/\/\S+\w)/','$1[[$2]]',$raw); // add markup to raw url
00585                 # restore preformatted blocks
00586                 $raw = preg_replace($this->_pre_markers,$this->_pre_markup,$raw);
00587                 # get marker data and offsets
00588                 $markerdata = $this->_markerdata = new StdClass();
00589                 $markerdata->offset = 0;
00590                 $markerdata->markercount = 0;
00591                 $this->_markers = array();
00592                 // pull out markers {{##markername marker decoration##}}
00593                 $re = '/(?P<text>[^{]*)|(?<!~)(?P<marker>\{\{##(?P<markername>[a-zA-Z]\w*)(\s+(?P<decorator>['.$this->_argchars.']+?))?\s*##\}\})|(?P<char>.)/';
00594                 $raw = preg_replace_callback($re,array($this,'preprocess_markerdata'),$raw);
00595                 $markerdata->markers = $this->_markers;
00596                 $this->_markers = NULL;
00597                 $markerdata->textlength = $markerdata->offset;
00598                 unset($markerdata->offset);
00599                 $this->_preprocessed_raw = $raw;
00600                 return $raw;
00601         }
00608         protected function preprocess_markerdata($matches)
00609         {
00610                 isset($matches['text']) or ($matches['text'] = '');
00611                 isset($matches['char']) or ($matches['char'] = '');
00612                 isset($matches['marker']) or ($matches['marker'] = '');
00613                 isset($matches['markername']) or ($matches['markername'] = '');
00614                 isset($matches['decorator']) or ($matches['decorator'] = '');
00615                 $text = $matches['text'].$matches['char'];
00616                 $this->_markerdata->offset += strlen($text);
00617                 if ($marker = $matches['marker'])
00618                 {
00619                         $this->_markerdata->markercount++;
00620                         $markerobject = $this->_markers[] = new StdClass();
00621                         $markerobject->offset = $this->_markerdata->offset;
00622                         $name = $markerobject->name = $matches['markername'];
00623                         $this->_markers[$name] = $markerobject;
00624                         if ($decorator = $matches['decorator']) 
00625                                 $markerobject->decoration = $this->get_decoration($decorator);
00626                         else
00627                                 $markerobject->decoration = NULL;
00628                 }
00629                 return $text;
00630         }
00637         protected function preprocess_metadata($matches)
00638         {
00639                 $arguments = trim($matches[1]);
00640                 // remove line continuations
00641                 $arguments = preg_replace('/\n``/','',$arguments);
00642                 // save data
00643                 $this->_metadata = $this->get_decoration($arguments);
00644                 return '';
00645         }
00650         public function get_decoration($decorator_string) 
00651         {
00652                 $decoration = new StdClass();
00653                 $decoration->classes = array();
00654                 $decoration->properties = array();
00655                 $decoration->attributes = array();
00656                 $terms = array();
00657                 preg_match_all($this->decorator_re, $decorator_string, $terms, PREG_SET_ORDER); // returns terms
00658                 foreach($terms as $term) 
00659                 {
00660                         isset($term['variable']) or ($term['variable'] = '');
00661                         isset($term['operator']) or ($term['operator'] = '');
00662                         isset($term['ddelim_value']) or ($term['ddelim_value'] = '');
00663                         isset($term['sdelim_value']) or ($term['sdelim_value'] = '');
00664                         isset($term['ndelim_value']) or ($term['ndelim_value'] = '');
00665                         $variable = $term['variable'];
00666                         $operator = $term['operator'];
00667                         if ($term['ddelim_value']) $delimiter = '"';
00668                         elseif ($term['sdelim_value']) $delimiter = "'";
00669                         else $delimiter = '';
00670                         // only one of the following will not be empty
00671                         $value = $term['ddelim_value'] . $term['sdelim_value'] . $term['ndelim_value']; 
00672                         switch ($operator)
00673                         {
00674                                 case '=':
00675                                         $decoration->attributes[$variable] = $value;
00676                                         if ($delimiter == '') $delimiter = '"';
00677                                         $decoration->attributedelimiters[$variable] = $delimiter;
00678                                         break;
00679                                 case ':':
00680                                         $decoration->properties[$variable] = $value;
00681                                         break;
00682                                 default:
00683                                         $decoration->classes[] = $value;
00684                                         break;
00685                         }
00686                 }
00687                 return $decoration;
00688         }
00694         protected function set_node_decoration($node,$decorator_string)
00695         {
00696                 $node->decoration = $this->get_decoration($decorator_string);
00697                 $node->decoration->markup = $decorator_string;
00698         }
00699         #------------------------------------------------------------------------------#
00700         #---------------------------[ utilities ]--------------------------------------#
00701         #------------------------------------------------------------------------------#
00702 
00710     public function get_selected_ancestor($node, $types) // public as can be used by registered callbacks
00711         {
00712         while ((!empty($node->parent)) and (!in_array($node->type,$types)))
00713                 {
00714             $node = $node->parent;
00715                 }
00716         return $node;
00717         }
00727         #=========================[ basic processing ]=================================#
00728 
00735     protected function _char_node($preg_groups) // can create text leaf node
00736         {
00737                 $char = $preg_groups['char'];
00738         if (is_null($this->_leaftextnode))
00739             $this->_leaftextnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TEXT, $this->_curnode);
00740         $this->_leaftextnode->textcontent .= $char;
00741         }
00750    protected function _escape_node($preg_groups)
00751         {
00752                 $char = $preg_groups['escaped_char'];
00753         if (is_null($this->_leaftextnode))
00754             $this->_leaftextnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TEXT, $this->_curnode);
00755         $this->_leaftextnode->textcontent .= $char;
00756         }
00762     protected function _blankline_node($preg_groups)
00763         {
00764                 # triggers new block
00765         $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
00766                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
00767         }
00782     protected function _paragraph_node($preg_groups) // can create paragraph for new text
00783         {
00784                 # text not otherwise classified, triggers creation of paragraph for new set
00785                 isset($preg_groups['text_chars']) or ($preg_groups['text_chars'] = '');
00786                 isset($preg_groups['text_charstream']) or ($preg_groups['text_charstream'] = '');
00787                 isset($preg_groups['paragraph_decorator']) or ($preg_groups['paragraph_decorator'] = '');
00788         $text = $preg_groups['text_chars'] . $preg_groups['text_charstream'];
00789                 $decorator = $preg_groups['paragraph_decorator'];
00790         if (in_array($this->_curnode->type, 
00791                         array(
00792                                 SimpleWiki_DocNode::TABLE, 
00793                                 SimpleWiki_DocNode::TABLE_ROW, 
00794                                 SimpleWiki_DocNode::UNORDERED_LIST, 
00795                                 SimpleWiki_DocNode::ORDERED_LIST,
00796                                 SimpleWiki_DocNode::DEF_LIST))) // text cannot exist in these blocks
00797                 {
00798             $this->_curnode = $this->get_selected_ancestor($this->_curnode,
00799                 array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
00800                 }
00801         if (in_array($this->_curnode->type, array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF)))
00802                 {
00803             $node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::PARAGRAPH, $this->_curnode);
00804                         if ($decorator) $this->set_node_decoration($node,$decorator);
00805                 } else {
00806                         $text = ' ' . $text;
00807                 }
00808         $this->_parse_inline($text);
00809         $this->_leaftextnode = NULL;
00810         }
00811         #================================[ core markup ]===============================#
00812         #--------------------------------[ basic markup ]------------------------------#
00813 
00823     protected function _heading_node($preg_groups)
00824         {
00825                 # headings
00826                 isset($preg_groups['heading_text']) or ($preg_groups['heading_text'] = '');
00827                 isset($preg_groups['heading_head']) or ($preg_groups['heading_head'] = '');
00828                 isset($preg_groups['heading_decorator']) or ($preg_groups['heading_decorator'] = '');
00829                 $headtext = $preg_groups['heading_text'];
00830                 $headhead = $preg_groups['heading_head'];
00831                 $decorator = $preg_groups['heading_decorator'];
00832                 
00833         $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
00834                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
00835                 
00836         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::HEADING,$this->_curnode);
00837         $node->level = strlen($headhead);
00838                 if ($decorator) $this->set_node_decoration($node,$decorator);
00839 
00840         $parent = $this->_curnode;
00841         $this->_curnode = $node;
00842         $this->_leaftextnode = NULL;
00843 
00844         $this->_parse_inline($headtext);
00845 
00846         $this->_curnode = $parent;
00847         $this->_leaftextnode = NULL;
00848         }
00856     protected function _emph_node($preg_groups)
00857         {
00858                 # emphasis
00859         if ($this->_curnode->type != SimpleWiki_DocNode::EMPHASIS)
00860             $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::EMPHASIS, $this->_curnode);
00861         else
00862                 {
00863                         if (!empty($this->_curnode->parent))
00864                                 $this->_curnode = $this->_curnode->parent;
00865                 }
00866         $this->_leaftextnode = NULL;
00867         }
00875     protected function _strong_node($preg_groups)
00876         {
00877                 # strong
00878         if ($this->_curnode->type != SimpleWiki_DocNode::STRONG)
00879             $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::STRONG, $this->_curnode);
00880         else
00881                 {
00882                         if (!empty($this->_curnode->parent))
00883                                 $this->_curnode = $this->_curnode->parent;
00884                 }
00885         $this->_leaftextnode = NULL;
00886         }
00892     protected function _linebreak_node($preg_groups)
00893         {
00894                 # line break
00895         new SimpleWiki_DocNode(SimpleWiki_DocNode::LINEBREAK, $this->_curnode);
00896         $this->_leaftextnode = NULL;
00897         }
00904     protected function _horizontalrule_node($preg_groups)
00905         {
00906         $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
00907                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
00908         new SimpleWiki_DocNode(SimpleWiki_DocNode::HORIZONTALRULE, $this->_curnode);
00909         }
00918     protected function _link_node($preg_groups)
00919         {
00920         # Handle all types of links.
00921                 isset($preg_groups['link_target']) or ($preg_groups['link_target'] = '');
00922                 isset($preg_groups['link_text']) or ($preg_groups['link_text'] = '');
00923                 isset($preg_groups['link_title']) or ($preg_groups['link_title'] = '');
00924                 isset($preg_groups['link_decorator']) or ($preg_groups['link_decorator'] = '');
00925         $target = trim($preg_groups['link_target']);
00926         $text = trim($preg_groups['link_text']);
00927                 $title = trim($preg_groups['link_title']);
00928                 $decorator = trim($preg_groups['link_decorator']);
00929 
00930                 $node =  new SimpleWiki_DocNode(SimpleWiki_DocNode::LINK, $this->_curnode);
00931                 $node->target = $target;
00932                 if ($decorator) $this->set_node_decoration($node,$decorator);
00933                 if ($title) $node->title = $title;
00934 
00935         $parent = $this->_curnode;
00936         $this->_curnode = $node;
00937         $this->_leaftextnode = NULL;
00938 
00939         preg_replace_callback($this->link_inline_re, array($this,'_create_node'), $text);
00940 
00941         $this->_curnode = $parent;
00942         $this->_leaftextnode = NULL;
00943         }
00944         #--------------------------------[ images ]-------------------------------------#
00945 
00953         protected function _image_node($preg_groups)
00954         {
00955         # Handles images included in the page.
00956                 isset($preg_groups['image_target']) or ($preg_groups['image_target'] = '');
00957                 isset($preg_groups['image_text']) or ($preg_groups['image_text'] = '');
00958                 isset($preg_groups['image_title']) or ($preg_groups['image_title'] = '');
00959                 isset($preg_groups['image_decorator']) or ($preg_groups['image_decorator'] = '');
00960         $target = trim($preg_groups['image_target']);
00961         $text = trim($preg_groups['image_text']);
00962                 $title = trim($preg_groups['image_title']);
00963                 $decorator = trim($preg_groups['image_decorator']);
00964 
00965         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::IMAGE, $this->_curnode);
00966                 $node->target = $target;
00967                 if ($decorator) $this->set_node_decoration($node,$decorator);
00968                 if ($title != '') $node->title = $title;
00969 
00970         $parent = $this->_curnode;
00971         $this->_curnode = $node;
00972         $this->_leaftextnode = NULL;
00973 
00974         preg_replace_callback($this->image_inline_re, array($this,'_create_node'), $text);
00975 
00976         $this->_curnode = $parent;
00977         $this->_leaftextnode = NULL;
00978         }
00979         #--------------------------------[ lists ]-------------------------------------#
00980 
00985     protected function _list_node($preg_groups)
00986         {
00987                 # collect list markup, detail processing by item
00988         $text = $preg_groups['list'];
00989         preg_replace_callback($this->item_inline_re,array($this,'_create_node'), $text);
00990         }
01000    protected function _item_node($preg_groups)
01001         {
01002                 # list item
01003                 isset($preg_groups['item_head']) or ($preg_groups['item_head'] = '');
01004                 isset($preg_groups['item_text']) or ($preg_groups['item_text'] = '');
01005                 isset($preg_groups['list_decorator']) or ($preg_groups['list_decorator'] = '');
01006                 isset($preg_groups['item_decorator']) or ($preg_groups['item_decorator'] = '');
01007         $bullet = $preg_groups['item_head'];
01008         $text = $preg_groups['item_text'];
01009                 $listdecorator = $preg_groups['list_decorator'];
01010                 $itemdecorator = $preg_groups['item_decorator'];
01011                 // determine the type of list being processed
01012         if ($bullet{0} == '#')
01013             $listtype = SimpleWiki_DocNode::ORDERED_LIST;
01014         else
01015             $listtype = SimpleWiki_DocNode::UNORDERED_LIST;
01016                 // determine the level by measuring the number of list markup characters
01017         $level = strlen($bullet);
01018         # Find a node of the same type and level up the tree, or a block to start a list
01019         $candidate_node = $this->_curnode;
01020         while // find a reference node if current list doesn't match, and if we're not in a block node to start
01021                 (
01022                         ($candidate_node) // searching an existing node
01023                         and ! // this is a not a list of the same level...
01024                         (
01025                                 in_array($candidate_node->type, array(SimpleWiki_DocNode::ORDERED_LIST, SimpleWiki_DocNode::UNORDERED_LIST)) 
01026                                 and $candidate_node->level == $level
01027                         )
01028                         and ! // ... and this is not a block ...
01029                         (
01030                                 in_array($candidate_node->type, array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF))
01031                         )
01032                 ) // ... so keep looking.
01033                 {
01034             $candidate_node = $candidate_node->parent;
01035                 }
01036                 # set the found list as the current node for the list item... 
01037                 # (if $candidate_node is null then no reference candidate was found)
01038         if ($candidate_node and ($candidate_node->type == $listtype)) // found a match for list
01039             $this->_curnode = $candidate_node;
01040         else # ... or create a new level of list
01041                 {
01042                         // get the nearest ancestor candidate for creating a new list
01043             $this->_curnode = $this->get_selected_ancestor($this->_curnode,
01044                 array(SimpleWiki_DocNode::LIST_ITEM, SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
01045                         // create the list
01046             $listnode = $this->_curnode = new SimpleWiki_DocNode($listtype, $this->_curnode);
01047                         if ($listdecorator) $this->set_node_decoration($listnode,$listdecorator);
01048             $listnode->level = $level;
01049                 }
01050                 # now add the list item to the list
01051         $itemnode = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::LIST_ITEM, $this->_curnode);
01052                 if ($itemdecorator) $this->set_node_decoration($itemnode,$itemdecorator);
01053                 $this->_leaftextnode = NULL;
01054                 # parse the text of the list item
01055         $this->_parse_inline($text);
01056         $this->_leaftextnode = NULL;
01057         }
01058         #--------------------------------[ definition list ]-------------------------------------#
01059 
01064     protected function _deflist_node($preg_groups)
01065         {
01066                 # collect list markup, detail processing by item
01067         $text = $preg_groups['deflist'];
01068         preg_replace_callback($this->defitem_inline_re,array($this,'_create_node'), $text);
01069         }
01080     protected function _defitem_node($preg_groups)
01081         {
01082                 # list item
01083                 isset($preg_groups['defitem_head']) or ($preg_groups['defitem_head'] = '');
01084                 isset($preg_groups['defterm_text']) or ($preg_groups['defterm_text'] = '');
01085                 isset($preg_groups['defdesc_text']) or ($preg_groups['defdesc_text'] = '');
01086                 isset($preg_groups['deflist_decorator']) or ($preg_groups['deflist_decorator'] = '');
01087                 isset($preg_groups['defterm_decorator']) or ($preg_groups['defterm_decorator'] = '');
01088                 isset($preg_groups['defdesc_decorator']) or ($preg_groups['defdesc_decorator'] = '');
01089         $head = $preg_groups['defitem_head'];
01090         $term = trim($preg_groups['defterm_text']);
01091         $desc = $preg_groups['defdesc_text'];
01092                 $listdecorator = $preg_groups['deflist_decorator'];
01093                 $termdecorator = $preg_groups['defterm_decorator'];
01094                 $descdecorator = $preg_groups['defdesc_decorator'];
01095                 // set the type of list being processed
01096         $listtype = SimpleWiki_DocNode::DEF_LIST;
01097                 // determine the level by measuring the number of list markup characters
01098         $level = strlen($head);
01099         # Find a node of the same type and level up the tree, or a block to start a list
01100         $candidate_node = $this->_curnode;
01101         while // find a reference node if current list doesn't match, and if we're not in a block node to start
01102                 (
01103                         ($candidate_node) // searching an existing node
01104                         and ! // this is a not a list of the same level...
01105                         (
01106                                 ($candidate_node->type == SimpleWiki_DocNode::DEF_LIST) 
01107                                 and $candidate_node->level == $level
01108                         )
01109                         and ! // ... and this is not a block ...
01110                         (
01111                                 in_array($candidate_node->type, array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF))
01112                         )
01113                 ) // ... so keep looking.
01114         $candidate_node = $candidate_node->parent;
01115                 # set the found list as the current node for the list item... 
01116                 # (if $candidate_node is null then no reference candidate was found)
01117         if ($candidate_node and ($candidate_node->type == $listtype)) // found a match for list
01118             $this->_curnode = $candidate_node;
01119         else # ... or create a new level of list
01120                 {
01121                         // get the nearest ancestor candidate for creating a new list
01122             $this->_curnode = $this->get_selected_ancestor($this->_curnode,
01123                 array(SimpleWiki_DocNode::DEF_DESC, SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
01124                         // create the list
01125             $listnode = $this->_curnode = new SimpleWiki_DocNode($listtype, $this->_curnode);
01126                         if ($listdecorator) $this->set_node_decoration($listnode,$listdecorator);
01127             $listnode->level = $level;
01128                 }
01129                 # now add the term to the list, if present
01130                 if ($term)
01131                 {
01132                         $curnode = $this->_curnode;
01133                         $termnode = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::DEF_TERM, $this->_curnode);
01134                         if ($termdecorator) $this->set_node_decoration($termnode,$termdecorator);
01135                         $this->_leaftextnode = NULL;
01136                         # parse the text of the term
01137                         $this->_parse_inline($term);
01138                         $this->_leaftextnode = NULL;
01139                         $this->_curnode = $curnode;
01140                 }
01141                 # ...and add the desc to the list
01142                 $descnode = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::DEF_DESC, $this->_curnode);
01143                 if ($descdecorator) $this->set_node_decoration($descnode,$descdecorator);
01144                 $this->_leaftextnode = NULL;
01145                 # parse the text of the desc
01146                 $this->_parse_inline($desc);
01147                 $this->_leaftextnode = NULL;
01148         }
01149         #--------------------------------[ tables ]-------------------------------------#
01150 
01161         protected function _table_node($preg_groups)
01162         {
01163                 # process a table row (any line beginning with '|')
01164                 isset($preg_groups['table_row']) or ($preg_groups['table_row'] = '');
01165                 isset($preg_groups['table_decorator']) or ($preg_groups['table_decorator'] = '');
01166                 isset($preg_groups['row_decorator']) or ($preg_groups['row_decoratpor'] = '');
01167                 $rowmarkup = trim($preg_groups['table_row']);
01168                 # set aside rowmarkup links, preformats, macros and images to simplify markup
01169                 $rowmarkup = preg_replace_callback(
01170                         $this->tablerow_setaside_re, array($this,'add_tablerow_markers'),$rowmarkup);
01171                 # assure at least content of a space in every cell.
01172                 $rowmarkup = preg_replace('/((?<!:)\|(?=\|))/','| ',$rowmarkup); // ensure content for every cell
01173                 $tabledecorator = trim($preg_groups['table_decorator']);
01174                 $rowdecorator = trim($preg_groups['row_decorator']);
01175                 
01176                 # set reference node to nearest table, document, or block ancestor
01177                 $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
01178                         array(SimpleWiki_DocNode::TABLE,SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
01179                 # create new table node if necessary
01180                 if ($this->_curnode->type != SimpleWiki_DocNode::TABLE)
01181                         $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TABLE, $this->_curnode);
01182                 # set decoration for table
01183                 $tablenode = $this->_curnode;
01184                 if ($tabledecorator) $this->set_node_decoration($tablenode,$tabledecorator);
01185                 
01186                 # create a new row node
01187                 $row_node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TABLE_ROW, $tablenode);
01188                 # add decoration to new row
01189                 if ($rowdecorator) $this->set_node_decoration($row_node,$rowdecorator);
01190                 
01191                 # collect all cell markup into $cell_matches
01192                 preg_match_all($this->cell_re, $rowmarkup, $cell_matches, PREG_SET_ORDER);
01193                 # process cell_matches
01194                 $this->_leaftextnode = NULL;
01195                 foreach ($cell_matches as $cell_groups) {
01196                         # get cell markup
01197                         isset($cell_groups['cell']) or ($cell_groups['cell'] = '');
01198                         isset($cell_groups['head']) or ($cell_groups['head'] = '');
01199                         isset($cell_groups['cell_decorator']) or ($cell_groups['cell_decorator'] = '');
01200                         $cellmarkup = $cell_groups['cell'];
01201                         $cellhead = $cell_groups['head'];
01202                         $celldecorator = $cell_groups['cell_decorator'];
01203                         # create table header cell or table data cell
01204                         if ($cellhead) {
01205                                 $cellmarkup = trim($cellhead,'=');
01206                                 $cell_node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TABLE_HEADCELL, $row_node);
01207                         } else {
01208                                 $cell_node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TABLE_CELL, $row_node);
01209                         }
01210                         # apply decoration to cell node
01211                         if ($celldecorator) $this->set_node_decoration($cell_node,$celldecorator);
01212                         # restore links, preformats, macros and images to current cell
01213                         $cellmarkup = preg_replace($this->_tablerow_markers,$this->_tablerow_markup,$cellmarkup);
01214                         # process cell inline markup
01215                         $this->_leaftextnode = NULL;
01216                         preg_replace_callback($this->inline_re, array($this,'_create_node'), $cellmarkup);
01217                 }
01218                 # set reference back to table node
01219                 $this->_curnode = $tablenode;
01220                 $this->_leaftextnode = NULL;
01221                 # reset table setaside structure
01222                 $this->_tablerow_markers = array();
01223                 $this->_tablerow_markup = array();
01224                 $this->_tablerow_count = 0;
01225         }
01226         #================================[ special decorators ]=============================#
01227         #--------------------------------[ span decoration ]--------------------------------#
01228 
01237         protected function _span_node($preg_groups)
01238         {
01239                 # span
01240                 isset($preg_groups['span_decorator']) or ($preg_groups['span_decorator'] = '');
01241                 $decorator = $preg_groups['span_decorator'];
01242         if ($decorator) // new span
01243                 {
01244             $node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::SPAN, $this->_curnode);
01245                         $this->set_node_decoration($node,$decorator);
01246                         $this->_leaftextnode = NULL;
01247                 }
01248         elseif ($this->_curnode->type == SimpleWiki_DocNode::SPAN) // closing existing span
01249                 {
01250                         if (!empty($this->_curnode->parent))
01251                         {
01252                                 $this->_curnode = $this->_curnode->parent;
01253                                 $this->_leaftextnode = NULL;
01254                         }
01255                 }
01256                 else // error, return text
01257                 {
01258                         if (is_null($this->_leaftextnode))
01259                                 $this->_leaftextnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::TEXT, $this->_curnode);
01260                         $this->_leaftextnode->textcontent .= $preg_groups['span'];
01261                 }
01262         }
01263         #--------------------------------[ block dividers ]--------------------------------#
01264 
01272     protected function _blockdivider_node($preg_groups)
01273         {
01274                 # empty block acting as block divider
01275                 $decorator = $preg_groups['blockdivider_decorator'];
01276         $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
01277                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
01278         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::BLOCKDIVIDER, $this->_curnode);
01279                 if ($decorator) $this->set_node_decoration($node,$decorator);
01280         }
01281         #============================[ preformatted text ]=================================#
01282 
01289     protected function _code_node($preg_groups)
01290         {
01291                 # preformatted inline text
01292                 isset($preg_groups['code_text']) or ($preg_groups['code_text'] = '');
01293                 isset($preg_groups['code_decorator']) or ($preg_groups['code_decorator'] = '');
01294                 $codetext = $preg_groups['code_text'];
01295                 $decorator = trim($preg_groups['code_decorator']);
01296                 
01297         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::CODE, $this->_curnode);
01298                 $node->textcontent = $codetext;
01299                 if ($decorator) $this->set_node_decoration($node,$decorator);
01300         $this->_leaftextnode = NULL;
01301         }
01309     protected function _pre_node($preg_groups)
01310         {
01311                 # process preformatted text
01312                 isset($preg_groups['pre_type']) or ($preg_groups['pre_type'] = '');
01313                 isset($preg_groups['pre_markup']) or ($preg_groups['pre_markup'] = '');
01314                 isset($preg_groups['pre_decorator']) or ($preg_groups['pre_decorator'] = '');
01315         $type = $preg_groups['pre_type'];
01316         $text = $preg_groups['pre_markup'];
01317                 $decorator = $preg_groups['pre_decorator'];
01318                 
01319         $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
01320                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF));
01321         $text = preg_replace_callback($this->pre_escape_re,array($this,'remove_tilde'), $text);
01322         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::PREFORMATTED, $this->_curnode);
01323                 $node->textcontent = $text;
01324         $node->section = $type?$type:'';
01325                 if ($decorator) $this->set_node_decoration($node,$decorator);
01326         $this->_leaftextnode = NULL;
01327         }
01330     private function remove_tilde($preg_groups)
01331         {
01332                 # used in pre processing of pre element
01333         return $preg_groups['indent'] . $preg_groups['rest'];
01334         }
01335         #================================[ advanced markup ]===============================#
01336         #--------------------------------[ block declarations ]------------------------------#
01337 
01345         protected function _blockdef_node($preg_groups)
01346         {
01347                 # block definitions
01348                 isset($preg_groups['block_selector']) or ($preg_groups['block_selector'] = '');
01349                 isset($preg_groups['block_content']) or ($preg_groups['block_content'] = '');
01350                 isset($preg_groups['block_decorator']) or ($preg_groups['block_decorator'] = '');
01351                 isset($preg_groups['block_inline']) or ($preg_groups['block_inline'] = '');
01352                 $name = $preg_groups['block_selector'];
01353                 $content = $preg_groups['block_content'];
01354                 $decorator = $preg_groups['block_decorator'];
01355                 $inline = $preg_groups['block_inline'];
01356                 
01357         $container = $this->_curnode = $this->get_selected_ancestor($this->_curnode,
01358             array(SimpleWiki_DocNode::DOCUMENT,
01359                                 SimpleWiki_DocNode::BLOCKDEF,
01360                                 SimpleWiki_DocNode::LIST_ITEM,
01361                                 SimpleWiki_DocNode::DEF_DESC));
01362         $node = $this->_curnode = new SimpleWiki_DocNode(SimpleWiki_DocNode::BLOCKDEF, $container);
01363                 $node->blocktag = $name;
01364                 if ($decorator) $this->set_node_decoration($node,$decorator);
01365                 
01366                 $this->_leaftextnode = NULL;
01367         if ($inline) $this->_parse_inline($inline);
01368                 $this->_leaftextnode = NULL;
01369         if ($content) $this->_parse_block($content);
01370                 $this->_curnode = $container;
01371         $this->_leaftextnode = NULL;
01372                 
01373         }
01374         #-----------------------------------[ macros ]-------------------------------------#
01375 
01384         protected function _macro_node($preg_groups)
01385         {
01386         # Handles macros using the placeholder syntax.
01387                 isset($preg_groups['macro_name']) or ($preg_groups['macro_name'] = '');
01388                 isset($preg_groups['macro_text']) or ($preg_groups['macro_text'] = '');
01389                 isset($preg_groups['macro_args']) or ($preg_groups['macro_args'] = '');
01390         $name = $preg_groups['macro_name'];
01391         $text = trim($preg_groups['macro_text']);
01392                 $decorator = $preg_groups['macro_args'];
01393 
01394                 $container = $this->_curnode;
01395         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::MACRO, $container);
01396                 $node->macroname = $name;
01397 
01398         if ($decorator) $this->set_node_decoration($node,$decorator);
01399                 if ($text)
01400                 {
01401                         $node->textcontent = $text;
01402                         $this->_curnode = $node;
01403                         $this->_leaftextnode = NULL;
01404                         $this->_parse_inline($text);
01405                         $this->_curnode = $container;
01406                 }
01407         $this->_leaftextnode = NULL;
01408         }
01418         protected function _blockmacro_node($preg_groups)
01419         {
01420         # Handles macros using the placeholder syntax. block version
01421                 isset($preg_groups['blockmacro_name']) or ($preg_groups['blockmacro_name'] = '');
01422                 isset($preg_groups['blockmacro_text']) or ($preg_groups['blockmacro_text'] = '');
01423                 isset($preg_groups['blockmacro_args']) or ($preg_groups['blockmacro_args'] = '');
01424         $name = $preg_groups['blockmacro_name'];
01425         $text = trim($preg_groups['blockmacro_text']);
01426                 $decorator = $preg_groups['blockmacro_args'];
01427                 
01428         $container = $this->_curnode = $this->get_selected_ancestor($this->_curnode, 
01429                         array(SimpleWiki_DocNode::DOCUMENT,SimpleWiki_DocNode::BLOCKDEF)); // different from macro
01430         $node = new SimpleWiki_DocNode(SimpleWiki_DocNode::MACRO, $this->_curnode);
01431                 $node->macroname = $name;
01432         if ($decorator) $this->set_node_decoration($node,$decorator);
01433                 if ($text)
01434                 {
01435                         $node->textcontent = $text;
01436                         $this->_curnode = $node;
01437                         $this->_leaftextnode = NULL;
01438                         $this->_parse_inline($text);
01439                         $this->_curnode = $container;
01440                 }
01441         $this->_leaftextnode = NULL;
01442         }
01446         #------------------------------------------------------------------------------#
01447         #---------------------------[ debug functions ]--------------------------------#
01448         #------------------------------------------------------------------------------#
01449 
01451         public function display_regex() // for debug
01452         {
01453                 echo 'BLOCK_RE ';
01454                 var_dump($this->block_re);
01455                 echo 'INLINE_RE ';
01456                 var_dump($this->inline_re);
01457                 echo 'link_inline_re ';
01458                 var_dump($this->link_inline_re);
01459                 echo 'item_inline_re ';
01460                 var_dump($this->image_inline_re);
01461                 echo 'item_inline_re ';
01462                 var_dump($this->item_inline_re);
01463                 echo 'defitem_inline_re ';
01464                 var_dump($this->defitem_inline_re);
01465                 echo 'CELL_RE ';
01466                 var_dump($this->cell_re);
01467                 echo 'PRE_ESCAPE_RE ';
01468                 var_dump($this->pre_escape_re);
01469                 echo 'DECORATOR_RE ';
01470                 var_dump($this->decorator_re);
01471                 echo 'TABLEROW_SETASIDE_RE ';
01472                 var_dump($this->tablerow_setaside_re);
01473         }
01476         public function display_dom($root) // for debug
01477         {
01478                 $count = 1;
01479                 $rootarray = array();
01480                 $count += $this->display_dom_add_child($root,$rootarray);
01481                 $rootarray = $rootarray[0];
01482                 print_r($rootarray);
01483                 return $count;
01484         }
01488         protected function display_dom_add_child($node,&$childarray) // for debug
01489         {
01490                 $nodearray = $node->get_display_list();
01491                 $children = $node->children;
01492                 $count = 0;
01493                 if (!empty($children))
01494                 {
01495                         $nodearray['children'] = array();
01496                         foreach ($children as $child)
01497                                 $count+= $this->display_dom_add_child($child,$nodearray['children']);
01498                 }
01499                 $childarray[] = $nodearray;
01500                 return count($children) + $count;
01501         }
01503 }
01504