Simplewiki  1.1
emitter.php
Go to the documentation of this file.
1 <?php
2 // Muster Software Copyright (c) Henrik Bechmann, Toronto, Canada 2009-2012. All rights reserved.
3 // See "musterlicence.txt" for licencing information.
4 // mustersoftware.net
5 
6 namespace Muster\Simplewiki;
7 
8 use StdClass;
9 
14 #==========================================================================#
15 #-----------------------------[ EMITTER ]----------------------------------#
16 #==========================================================================#
17 
22 // generates the HTML; other emitters could be substituted
23 class Emitter
24 {
26 
27  protected $_dom;
29  protected $_rules;
31  protected $_link_re;
33  protected $_image_re;
35  protected $_blocktags = // could be replaced or changed by client
36  array
37  (
38  'div', 'blockquote', # division, blockquote
39  'table', 'thead', 'tbody', 'tr', 'td', 'th', 'tfoot', 'caption', # table
40  'ul', 'ol', 'li', 'dl', 'dt', 'dd', # lists
41  'dlmodule','dlwidget','dlsettings','dlmarker' #dlml
42  );
45 
46  protected $_class_callouts = array();
48  protected $_property_callouts = array();
50  protected $_macro_callouts = array();
52  protected $_symlinks = array();
54  protected $_symlink_handler;
56  protected $_rawlink_handler;
60  protected $_events = array();
62  protected $_blockdef_handler;
70  public function __construct()
71  {
72  $this->set_rules();
73  $this->set_re($this->_rules);
74  }
75  #----------------------------------[ init ]-----------------------------------------#
76 
79  protected function set_rules()
80  {
81  $rules = new StdClass;
82  $proto = 'http[s]?|ftp|nntp|news|telnet|file|irc';
83  $rules->extern = "(?P<external_address>(?P<external_proto>$proto)
84  :\/\/(?P<external_selector>\S+\w))";
85  $rules->mail = "(?P<external_mailaddress>(?P<external_mailproto>mailto)
86  :(?P<external_mailselector>\S+\w))";
87  $rules->symlink = '
88  ((?P<internal_address> (?P<symlink>[A-Z][a-zA-Z-]+) :
89  (?P<internal_selector> [^`\s]+))(`(?P<internal_version>\S+))?)
90  ';
91  $rules->anchor = '
92  (?P<anchor>\#[a-zA-Z][\w-]*)
93  ';
94  $rules->rawlink = '
95  (?P<rawlink>\S*)
96  ';
97  $this->_rules = $rules;
98  }
102  protected function set_re($rules)
103  {
104  $this->_link_re = '/' . implode('|',array($rules->extern,$rules->mail,$rules->symlink,$rules->anchor,$rules->rawlink)) . '/x';
105  $this->_image_re = '/' . implode('|',array($rules->extern,$rules->symlink,$rules->rawlink)) . '/x';
106  }
113  public function emit($dom) // main method
114  {
115  $this->_dom = $dom;
116  return $this->emit_node($dom);
117  }
122  protected function emit_node($node) // controller
123  {
124  $emit = '_' . $node->type . '_emit';
125  return $this->$emit($node);
126  }
131  public function emit_children($node)
132  {
133  if (empty($node->children)) return '';
134  $children = $node->children;
135  $childoutput = array();
136  foreach ($children as $child)
137  {
138  $childoutput[] = $this->emit_node($child);
139  }
140  return implode('',$childoutput);
141  }
144  public function emit_node_text($node)
145  {
146  if ($node->type == DocNode::TEXT)
147  return $node->textcontent;
148  else
149  return $this->emit_children_text($node);
150  }
154  protected function emit_children_text($node)
155  {
156  if (empty($node->children)) return '';
157  $children = $node->children;
158  $childoutput = array();
159  foreach ($children as $child)
160  {
161  $childoutput[] = $this->emit_node_text($child);
162  }
163  return implode(' ',$childoutput);
164  }
167 
168  public function symlinks()
169  {
170  return $this->_symlinks;
171  }
173  public function symlink_handler()
174  {
176  }
178  public function blocktags($blocktaglist = NULL)
179  {
180  if (!is_null($blocktaglist))
181  $this->_blocktags = $blocktaglist;
182  return $this->_blocktags;
183  }
185  #========================[ callout handling ]=======================================#
186 
192  public function register_events($callbacks)
193  {
194  $events = $this->_events;
195  foreach ($callbacks as $eventname => $callback)
196  {
197  if (!isset($events[$eventname]))
198  {
199  $events[$eventname] = array();
200  }
201  $events[$eventname][] = $callback;
202  }
203  $this->_events = $events;
204  }
207  public function register_symlinks($symlinks)
208  {
209  $symlinklist = $this->_symlinks;
210  foreach ($symlinks as $symlink => $value) {
211  $symlinklist[$symlink] = $value;
212  }
213  $this->_symlinks = $symlinklist;
214  }
218  public function register_symlink_handler($handler)
219  {
220  $this->_symlink_handler = $handler;
221  }
225  public function register_charfilter_handler($handler)
226  {
227  $this->_charfilter_handler = $handler;
228  }
232  public function register_rawlink_handler($handler)
233  {
234  $this->_rawlink_handler = $handler;
235  }
239  public function register_class_callouts($callouts)
240  {
241  // ['nodetype']['class']=>$methodref
242  $calloutlist = $this->_class_callouts;
243  foreach ($callouts as $nodetype => $classcallouts)
244  {
245  if (empty($calloutlist[$nodetype]))
246  {
247  $calloutlist[$nodetype] = $callouts[$nodetype];
248  }
249  else
250  {
251  $classcallouts = $callouts[$nodetype];
252  foreach($classcallouts as $class =>$methodref)
253  {
254  $calloutlist[$nodetype][$class] = $methodref;
255  }
256  }
257  }
258  $this->_class_callouts = $calloutlist;
259  }
263  public function register_property_callouts($callouts)
264  {
265  // ['nodetype']['property']=>$methodref
266  $calloutlist = $this->_property_callouts;
267  foreach ($callouts as $nodetype => $propertycallouts)
268  {
269  if (empty($calloutlist[$nodetype]))
270  {
271  $calloutlist[$nodetype] = $callouts[$nodetype];
272  }
273  else
274  {
275  $propertycallouts = $callouts[$nodetype];
276  foreach($propertycallouts as $class =>$methodref)
277  {
278  $calloutlist[$nodetype][$class] = $methodref;
279  }
280  }
281  }
282  $this->_property_callouts = $calloutlist;
283  }
286  public function register_macro_callouts($callouts)
287  {
288  // ['macroname']=>$methodref
289  $calloutlist = $this->_macro_callouts;
290  foreach ($callouts as $macroname => $methodref)
291  {
292  $calloutlist[$macroname] = $methodref;
293  }
294  $this->_macro_callouts = $calloutlist;
295  }
300  public function register_blockdef_handler($handler)
301  {
302  $this->_blockdef_handler = $handler;
303  }
305  #---------------------------[trigger callouts]-------------------------------------#
306 
312  protected function expand_symlink($node)
313  {
314  $symlinks = $this->_symlinks;
315  if (isset($symlinks[$node->linkparts->symlink]))
316  $node->linkparts->symlinkpath = $symlinks[$node->linkparts->symlink];
317  elseif (isset($this->_symlink_handler))
318  $node = call_user_func($this->_symlink_handler,$node);
319  else {
320  $node->unknown = TRUE;
321  $node->linkparts->symlinkpath = $node->linkparts->symlink . ':'; // stub
322  }
323  return $node;
324  }
330  protected function expand_rawlink($node)
331  {
332  if (isset($this->_rawlink_handler))
333  $node = call_user_func($this->_rawlink_handler,$node);
334  else {
335  $node->unknown = TRUE;
336  $node->linkparts->rawlinkaddress = $node->linkparts->rawlink; // stub
337  }
338  return $node;
339  }
345  protected function call_macro($node)
346  {
347  $callbacks = $this->_macro_callouts;
348  if (isset($callbacks[$node->macroname]))
349  {
350  $node = call_user_func($callbacks[$node->macroname],$node);
351  $node->processed = TRUE;
352  }
353  return $node;
354  }
359  protected function call_classes($node)
360  {
361  $callbacks = isset($this->_class_callouts[$node->type])?$this->_class_callouts[$node->type]:NULL;
362  if (!empty($callbacks))
363  {
364  $classes = !empty($node->decoration->classes)?$node->decoration->classes:NULL;
365  if (!empty($classes))
366  {
367  foreach ($classes as $class)
368  {
369  $callback = isset($callbacks[$class])?$callbacks[$class]:NULL;
370  if ($callback)
371  {
372  $node = call_user_func($callback,$node);
373  }
374  }
375  }
376  }
377  return $node;
378  }
384  protected function call_properties($node)
385  {
386  $callbacks = isset($this->_property_callouts[$node->type])?$this->_property_callouts[$node->type]:NULL;
387  if (!empty($callbacks))
388  {
389  $properties = !empty($node->decoration->properties)?$node->decoration->properties:NULL;
390  if (!empty($properties))
391  {
392  foreach ($properties as $propertyindex => $property)
393  {
394  $callback = isset($callbacks[$propertyindex])?$callbacks[$propertyindex]:NULL;
395  if ($callback)
396  {
397  $node = call_user_func($callback,$node);
398  }
399  }
400  }
401  }
402  return $node;
403  }
410  protected function call_event($event,$node)
411  {
412  $events = isset($this->_events[$event])?$this->_events[$event]:NULL;
413  if (!empty($events)) {
414  foreach ($events as $callback) {
415  $node = call_user_func($callback,$node);
416  }
417  }
418  return $node;
419  }
421  #--------------------------------[ utilities ]---------------------------------------#
422 
423 /* protected function get_value($value,$default)
424  {
425  return isset($value)? $value: $default;
426  }
427 */
434  protected function prepare_image_node($node)
435  {
436  # symlink for src
437  if (!empty($node->linkparts->symlink)) {
438  $node = $this->expand_symlink($node);
439  $node->decoration->attributes['src'] =
440  $node->linkparts->symlinkpath . $node->linkparts->internalselector;
441  $node->decoration->attributedelimiters['src'] = '"';
442  }
443  # external address for src
444  else {
445  $node->decoration->attributes['src'] =
446  !empty($node->linkparts->externaladdress)?$node->linkparts->externaladdress:NULL;
447  $node->decoration->attributedelimiters['src'] = '"';
448  }
449  # alt attribute
450  // caption, or...
451  if ($node->caption) {
452  $node->decoration->attributes['alt'] = substr($this->char_filter(strip_tags($node->caption),$node),0,60) . '...';
453  $node->decoration->attributedelimiters['alt'] = '"';
454  }
455  // ... target.
456  elseif ($node->target)
457  {
458  $node->decoration->attributes['alt'] = substr($this->char_filter(strip_tags($node->target),$node),0,60) . '...';
459  $node->decoration->attributedelimiters['alt'] = '"';
460  }
461  # title attribute
462  if (!empty($node->title)) {
463  $node->decoration->attributes['title'] = $node->title;
464  $node->decoration->attributedelimiters['title'] = '"';
465  }
466  # standard prepare node...
467  $node = $this->prepare_node($node);
468  return $node;
469  }
477  protected function prepare_link_node($node)
478  {
479  $attributename = 'href';
480  if (!empty($node->linkparts->anchor)) {
481  if (empty($node->caption)) {
482  $attributename = 'name';
483  $node->linkparts->anchor = substr($node->linkparts->anchor,1);
484  $node->decoration->attributes[$attributename] = $node->linkparts->anchor;
485  $node->decoration->attributedelimiters[$attributename] = '"';
486  } else {
487  $node->linkparts->symlink = 'Anchor';
488  $node = $this->expand_symlink($node);
489  $node->decoration->attributes[$attributename] = $node->linkparts->symlinkpath . $node->linkparts->anchor;
490  $node->decoration->attributedelimiters[$attributename] = '"';
491  }
492  }
493  elseif (!empty($node->linkparts->symlink)) {
494  $node = $this->expand_symlink($node);
495  $node->decoration->attributes[$attributename] =
496  $node->linkparts->symlinkpath . (!empty($node->linkparts->internalselector)?$node->linkparts->internalselector:'');
497  $node->decoration->attributedelimiters[$attributename] = '"';
498  }
499  elseif (!empty($node->linkparts->externaladdress)) {
500  $node->decoration->attributes[$attributename] =
501  !empty($node->linkparts->externaladdress)?$node->linkparts->externaladdress:NULL;
502  $node->decoration->attributedelimiters[$attributename] = '"';
503  }
504  else {
505  $node = $this->expand_rawlink($node);
506  $node->decoration->attributes[$attributename] =
507  !empty($node->linkparts->rawlinkaddress)?$node->linkparts->rawlinkaddress:NULL;
508  $node->decoration->attributedelimiters[$attributename] = '"';
509  }
510  if (!empty($node->title)) {
511  $node->decoration->attributes['title'] = $node->title;
512  $node->decoration->attributedelimiters['title'] = '"';
513  }
514  $node = $this->prepare_node($node);
515  return $node;
516  }
521  protected function prepare_macro_node($node)
522  {
523  $node->output = '';
524  $this->call_macro($node);
525  if (empty($node->output) and ($node->caption)) {
526  $node->output = $node->caption;
527  }
528  return $node;
529  }
537  protected function prepare_node($node)
538  {
539  # trigger callouts
540  $node = $this->call_classes($node);
541  $node = $this->call_properties($node);
542  # convert input decoration attributes, values, and properties into html attributes
543  $attributes = array();
544  // attributes
545  $attr = !empty($node->decoration->attributes)?$node->decoration->attributes:array();
546  $attrdelimiters = !empty($node->decoration->attributedelimiters)?$node->decoration->attributedelimiters:array();
547  foreach ($attr as $key => $value) {
548  $attributes[] = $key . '=' . $attrdelimiters[$key] . $value . $attrdelimiters[$key];
549  }
550  // classes
551  $values = array();
552  $values = !empty($node->decoration->classes)?$node->decoration->classes:array();
553  $classes = preg_replace('/"/','\\"',implode(' ',$values)); // escape embedded double quotes
554  if (!empty($classes)) $attributes[]='class="' . $classes . '"';
555  // styles
556  $properties = array();
557  $properties = !empty($node->decoration->properties)?$node->decoration->properties:array();
558  $styles = array();
559  foreach ($properties as $key => $value) {
560  $styles[] = $key . ':' . $value;
561  }
562  if (!empty($styles)) {
563  $style = implode(';',preg_replace('/"/','\\"',$styles)); // escape embedded double quotes
564  $attributes[] = 'style="' . $style . '"';
565  }
566  if (!empty($attributes)) $node->opentag_head .= ' ';
567  $node->attributes = $attributes;
568  return $node;
569  }
576  protected function standard_assembly($node)
577  {
578  return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail
579  . $node->elementcontent . $node->closetag;
580  }
584  protected function char_filter($text, $node = NULL)
585  {
586  if (isset($this->_charfilter_handler)) {
587  return call_user_func($this->_charfilter_handler,$text,$node);
588  } else {
589  return htmlspecialchars($text);
590  }
591  }
593  #------------------------------------------------------------------------------#
594  #-----------------------------[ node emitters ]--------------------------------#
595  #------------------------------------------------------------------------------#
596  #==============================[ document ]====================================#
597 
602  protected function _document_emit($node)
603  {
604  // anticipate event calls
605  $node->opentag_head = '';
606  $node->closetag = '';
607  $node = $this->call_event('onemit',$node);
608  $node->elementcontent = $this->emit_children($node);
609  $node = $this->call_event('onafteremit',$node);
610  return $node->opentag_head . $node->elementcontent . $node->closetag;
611  }
612  #=========================[ basic processing ]=================================#
613 
617  protected function _paragraph_emit($node) // b decorator "p"
618  {
619  $node->opentag_head = "\n<p";
620  $node->opentag_tail = ">";
621  $node->elementcontent = $this->emit_children($node);
622  $node->closetag = "</p>";
623  $node = $this->prepare_node($node);
624  return $this->standard_assembly($node);
625  }
630  protected function _text_emit($node)
631  {
632  return $this->char_filter($node->textcontent,$node);
633  }
634  #================================[ core markup ]===============================#
635  #--------------------------------[ basic markup ]------------------------------#
636 
640  protected function _heading_emit($node) // b decorator "h"
641  {
642  $node->opentag_head = "\n<h" . $node->level;
643  $node->opentag_tail = ">";
644  $node->elementcontent = $this->emit_children($node);
645  $node->closetag = "</h". $node->level . ">";
646  $node = $this->prepare_node($node);
647  return $this->standard_assembly($node);
648  }
653  protected function _emphasis_emit($node)
654  {
655  return "<em>" . $this->emit_children($node) . "</em>";
656  }
661  protected function _strong_emit($node)
662  {
663  return "<strong>" . $this->emit_children($node) . "</strong>";
664  }
669  protected function _linebreak_emit($node)
670  {
671  return "<br />\n";
672  }
677  protected function _horizontalrule_emit($node)
678  {
679  return "\n<hr />";
680  }
681  #--------------------------------[ links ]-------------------------------------#
682 
686  protected function _link_emit($node) // i decorator "a"
687  {
688  $node->caption = $this->emit_children($node);
689  // also available: $node->title
690  $address = $node->target;
691  $matches = array();
692  if (preg_match($this->_link_re,$address,$matches))
693  {
694  isset($matches['anchor']) or ($matches['anchor'] = '');
695  isset($matches['internal_address']) or ($matches['internal_address'] = '');
696  isset($matches['symlink']) or ($matches['symlink'] = '');
697  isset($matches['internal_selector']) or ($matches['internal_selector'] = '');
698  isset($matches['internal_version']) or ($matches['internal_version'] = '');
699  isset($matches['external_address']) or ($matches['external_address'] = '');
700  isset($matches['external_proto']) or ($matches['external_proto'] = '');
701  isset($matches['external_selector']) or ($matches['external_selector'] = '');
702  isset($matches['external_mailaddress']) or ($matches['external_mailaddress'] = '');
703  isset($matches['external_mailproto']) or ($matches['external_mailproto'] = '');
704  isset($matches['external_mailselector']) or ($matches['external_mailselector'] = '');
705  isset($matches['rawlink']) or ($matches['rawlink'] = '');
706 
707  $node->linkparts = new StdClass;
708  $node->linkparts->anchor = $matches['anchor'];
709 
710  $node->linkparts->internaladdress = $matches['internal_address'];
711  $node->linkparts->symlink = $matches['symlink'];
712  $node->linkparts->internalselector = $matches['internal_selector'];
713  $node->linkparts->internalversion = $matches['internal_version'];
714 
715  $node->linkparts->externaladdress = $matches['external_address'];
716  $node->linkparts->externalprotocol = $matches['external_proto'];
717  $node->linkparts->externalselector = $matches['external_selector'];
718 
719  if ($matches['external_mailaddress']) {
720  $node->linkparts->externaladdress = $matches['external_mailaddress'];
721  $node->linkparts->externalprotocol = $matches['external_mailproto'];
722  $node->linkparts->externalselector = $matches['external_mailselector'];
723  }
724 
725  $node->linkparts->rawlink = $matches['rawlink'];
726  }
727  if (empty($node->caption) and empty($node->linkparts->anchor)) {
728  if ($matches['external_mailaddress']) {
729  $node->caption = $node->linkparts->externalselector;
730  } else {
731  $node->caption = $node->target;
732  }
733  }
734 
735  $node->opentag_head = "<a";
736  $node->opentag_tail = ">";
737  $node->elementcontent = $node->caption;
738  $node->closetag = "</a>";
739  $node->unknown = FALSE;
740  $node = $this->prepare_link_node($node); // might set $node->unknown = TRUE;
741  if ($node->unknown) {
742  $node->opentag_head .= 'rel="nofollow" ';
743  $node->opentag_tail .= '<span style="border-bottom:1px dashed gray">';
744  $node->closetag = '</span><sup>?</sup>' . $node->closetag;
745  }
746  return $this->standard_assembly($node);
747  }
748  #--------------------------------[ images ]------------------------------------#
749 
753  protected function _image_emit($node) // i decorator "i"
754  {
755  $node->caption = $this->emit_children($node);
756  // also available: $node->title
757  $address = $node->target;
758  $matches = array();
759  if (preg_match($this->_image_re,$address,$matches))
760  {
761  isset($matches['internal_address']) or ($matches['internal_address'] = '');
762  isset($matches['symlink']) or ($matches['symlink'] = '');
763  isset($matches['internal_selector']) or ($matches['internal_selector'] = '');
764  isset($matches['internal_version']) or ($matches['internal_version'] = '');
765  isset($matches['external_address']) or ($matches['external_address'] = '');
766  isset($matches['external_proto']) or ($matches['external_proto'] = '');
767  isset($matches['external_selector']) or ($matches['external_selector'] = '');
768  isset($matches['rawlink']) or ($matches['rawlink'] = '');
769 
770  $node->linkparts = new StdClass;
771  $node->linkparts->internaladdress = $matches['internal_address'];
772  $node->linkparts->symlink = $matches['symlink'];
773  $node->linkparts->internalselector = $matches['internal_selector'];
774  $node->linkparts->internalversion = $matches['internal_version'];
775 
776  $node->linkparts->externaladdress = $matches['external_address'];
777  $node->linkparts->externalprotocol = $matches['external_proto'];
778  $node->linkparts->externalselector = $matches['external_selector'];
779 
780  $node->linkparts->rawlink = $matches['rawlink'];
781  }
782  $node->opentag_head = "<img";
783  $node->opentag_tail = "/>";
784  $node = $this->prepare_image_node($node);
785  return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail;
786  }
787  #--------------------------------[ lists ]-------------------------------------#
788 
792  protected function _def_list_emit($node) // b decorator "dl"
793  {
794  $node->opentag_head = "\n<dl";
795  $node->opentag_tail = ">";
796  $node->elementcontent = $this->emit_children($node);
797  $node->closetag = "\n</dl>";
798  $node = $this->prepare_node($node);
799  return $this->standard_assembly($node);
800  }
805  protected function _def_term_emit($node) // decorator "dt"
806  {
807  $node->opentag_head = "\n<dt";
808  $node->opentag_tail = ">";
809  $node->elementcontent = $this->emit_children($node);
810  $node->closetag = "</dt>";
811  $node = $this->prepare_node($node);
812  return $this->standard_assembly($node);
813  }
818  protected function _def_desc_emit($node) // decorator "dd"
819  {
820  $node->opentag_head = "\n<dd";
821  $node->opentag_tail = ">";
822  $node->elementcontent = $this->emit_children($node);
823  $node->closetag = "</dd>";
824  $node = $this->prepare_node($node);
825  return $this->standard_assembly($node);
826  }
831  protected function _ordered_list_emit($node) // b decorator "ol"
832  {
833  $node->opentag_head = "\n<ol";
834  $node->opentag_tail = ">";
835  $node->elementcontent = $this->emit_children($node);
836  $node->closetag = "\n</ol>";
837  $node = $this->prepare_node($node);
838  return $this->standard_assembly($node);
839 // return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail
840 // . $node->elementcontent . $node->closetag;
841  }
846  protected function _unordered_list_emit($node) // b decorator "ul"
847  {
848  $node->opentag_head = "\n<ul";
849  $node->opentag_tail = ">";
850  $node->elementcontent = $this->emit_children($node);
851  $node->closetag = "\n</ul>";
852  $node = $this->prepare_node($node);
853  return $this->standard_assembly($node);
854  }
859  protected function _list_item_emit($node) // decorator "li"
860  {
861  $node->opentag_head = "\n<li";
862  $node->opentag_tail = ">";
863  $node->elementcontent = $this->emit_children($node);
864  $node->closetag = "</li>";
865  $node = $this->prepare_node($node);
866  return $this->standard_assembly($node);
867  }
868  #--------------------------------[ tables ]------------------------------------#
869 
873  protected function _table_emit($node) // b decorator "table"
874  {
875  $node->opentag_head = "\n<table";
876  $node->opentag_tail = ">";
877  $node->elementcontent = $this->emit_children($node);
878  $node->closetag = "\n</table>";
879  $node = $this->prepare_node($node);
880  return $this->standard_assembly($node);
881  }
886  protected function _table_row_emit($node) // b decorator "tr"
887  {
888  $node->opentag_head = "\n<tr";
889  $node->opentag_tail = ">";
890  $node->elementcontent = $this->emit_children($node);
891  $node->closetag = "\n</tr>";
892  $node = $this->prepare_node($node);
893  return $this->standard_assembly($node);
894  }
899  protected function _table_headcell_emit($node) // b decorator "th"
900  {
901  $node->opentag_head = "\n<th";
902  $node->opentag_tail = ">\n";
903  $node->elementcontent = $this->emit_children($node);
904  $node->closetag = "</th>";
905  $node = $this->prepare_node($node);
906  return $this->standard_assembly($node);
907  }
912  protected function _table_cell_emit($node) // b decorator "td"
913  {
914  $node->opentag_head = "\n<td";
915  $node->opentag_tail = ">\n";
916  $node->elementcontent = $this->emit_children($node);
917  $node->closetag = "</td>";
918  $node = $this->prepare_node($node);
919  return $this->standard_assembly($node);
920  }
921  #=========================[ special decorators ]===============================#
922  #---------------------------[ span decoration ]--------------------------------#
923 
927  protected function _span_emit($node) // i decorator "s"
928  {
929  $node->opentag_head = "<span";
930  $node->opentag_tail = ">";
931  $node->elementcontent = $this->emit_children($node);
932  $node->closetag = "</span>";
933  $node = $node = $this->prepare_node($node);
934  return $this->standard_assembly($node);
935  }
936  #----------------------------[ block dividers ]--------------------------------#
937 
941  protected function _blockdivider_emit($node) // b decorator "b"
942  {
943  $node->opentag_head = "\n<div";
944  $node->opentag_tail = ">";
945  $node->elementcontent = '';
946  $node->closetag = "\n</div>";
947  $node = $this->prepare_node($node);
948  return $this->standard_assembly($node);
949  }
950  #============================[ preformatted text ]=============================#
951 
955  protected function _code_emit($node) // i decorator "c"
956  {
957  $node->opentag_head = "<code";
958  $node->opentag_tail = ">";
959  $node->elementcontent = $node->textcontent;
960  $node->escapecontent = TRUE;
961  $node->closetag = "</code>";
962  $node = $this->prepare_node($node);
963  if ($node->escapecontent) $node->elementcontent = $this->char_filter($node->elementcontent,$node);
964  return $this->standard_assembly($node);
965  }
970  protected function _preformatted_emit($node) // b decorator "pre"
971  {
972  $node->opentag_head = "\n<pre";
973  $node->opentag_tail = ">\n";
974  $node->elementcontent = $node->textcontent;
975  $node->escapecontent = TRUE;
976  $node->closetag = "</pre>";
977  $node = $this->prepare_node($node);
978  if ($node->escapecontent) $node->elementcontent = $this->char_filter($node->elementcontent,$node);
979  $retval = $this->standard_assembly($node);
980  return $retval;
981  }
982  #==============================[ advanced markup ]=============================#
983  #------------------------------[ block declarations ]----------------------------#
984 
988  protected function _blockdef_emit($node) // declaration decorator (various)
989  {
990  $blocktag = $node->blocktag;
991  $node->knowntag = in_array($blocktag,$this->_blocktags);
992  if (!$node->knowntag)
993  {
994  $node->opentag_head = "\n(:$blocktag " . $node->decoration->markup;
995  $node->opentag_tail = ":)";
996  $node->closetag = "\n(:{$blocktag}end:)";
997  }
998  elseif (substr($blocktag,0,2) == 'dl')
999  {
1000  $dlmltag = substr($blocktag,2);
1001  $node->opentag_head = "\n<dl:" . $dlmltag;
1002  $node->opentag_tail = ">";
1003  $node->closetag = "\n</dl:" . $dlmltag. '>';
1004  }
1005  else
1006  {
1007  $node->opentag_head = "\n<$blocktag";
1008  $node->opentag_tail = ">";
1009  $node->closetag = "\n</$blocktag>";
1010  }
1011  $node->elementcontent = '';
1012  if (isset($this->_blockdef_handler))
1013  $node = call_user_func($this->_blockdef_handler,$node);
1014  if (!$node->elementcontent) $node->elementcontent = $this->emit_children($node);
1015  if ($node->knowntag)
1016  {
1017  $node = $this->prepare_node($node);
1018  return $this->standard_assembly($node);
1019  }
1020  else
1021  {
1022  return $node->opentag_head . $node->opentag_tail
1023  . $node->elementcontent . $node->closetag;
1024  }
1025  }
1026  #--------------------------------[ macros ]--------------------------------#
1027 
1031  protected function _macro_emit($node) // macro decorator
1032  {
1033  $node->caption = $this->emit_children($node);
1034  $node->processed = FALSE; // set to TRUE by prepare_macro_node if macro found.
1035  $node = $this->prepare_macro_node($node);
1036  if ($node->processed)
1037  {
1038  return $node->output;
1039  } else { // return re-assembled source markup
1040  $opentag_head = '<<' . $node->macroname;
1041  $arguments = property_exists($node,"decoration")? $node->decoration->markup: '';
1042  if ($arguments != '') $arguments = ' ' . $arguments;
1043  $caption = $node->caption;
1044  if ($caption)
1045  $caption = '|' . $caption;
1046  $closetag = '>>';
1047  return $this->char_filter($opentag_head . $arguments . $caption . $closetag,$node);
1048  }
1049  }
1051 }