6 namespace Muster\Simplewiki;
14 #==========================================================================#
15 #-----------------------------[ EMITTER ]----------------------------------#
16 #==========================================================================#
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
73 $this->
set_re($this->_rules);
75 #----------------------------------[ init ]-----------------------------------------#
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))";
88 ((?P<internal_address> (?P<symlink>[A-Z][a-zA-Z-]+) :
89 (?P<internal_selector> [^`\s]+))(`(?P<internal_version>\S+))?)
92 (?P<anchor>\#[a-zA-Z][\w-]*)
97 $this->_rules = $rules;
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';
124 $emit =
'_' . $node->type .
'_emit';
125 return $this->$emit($node);
133 if (empty($node->children))
return '';
134 $children = $node->children;
135 $childoutput = array();
136 foreach ($children as $child)
138 $childoutput[] = $this->
emit_node($child);
140 return implode(
'',$childoutput);
147 return $node->textcontent;
156 if (empty($node->children))
return '';
157 $children = $node->children;
158 $childoutput = array();
159 foreach ($children as $child)
163 return implode(
' ',$childoutput);
180 if (!is_null($blocktaglist))
181 $this->_blocktags = $blocktaglist;
185 #========================[ callout handling ]=======================================#
195 foreach ($callbacks as $eventname => $callback)
197 if (!isset($events[$eventname]))
199 $events[$eventname] = array();
201 $events[$eventname][] = $callback;
203 $this->_events = $events;
210 foreach ($symlinks as $symlink => $value) {
211 $symlinklist[$symlink] = $value;
213 $this->_symlinks = $symlinklist;
220 $this->_symlink_handler = $handler;
227 $this->_charfilter_handler = $handler;
234 $this->_rawlink_handler = $handler;
243 foreach ($callouts as $nodetype => $classcallouts)
245 if (empty($calloutlist[$nodetype]))
247 $calloutlist[$nodetype] = $callouts[$nodetype];
251 $classcallouts = $callouts[$nodetype];
252 foreach($classcallouts as $class =>$methodref)
254 $calloutlist[$nodetype][$class] = $methodref;
258 $this->_class_callouts = $calloutlist;
267 foreach ($callouts as $nodetype => $propertycallouts)
269 if (empty($calloutlist[$nodetype]))
271 $calloutlist[$nodetype] = $callouts[$nodetype];
275 $propertycallouts = $callouts[$nodetype];
276 foreach($propertycallouts as $class =>$methodref)
278 $calloutlist[$nodetype][$class] = $methodref;
282 $this->_property_callouts = $calloutlist;
290 foreach ($callouts as $macroname => $methodref)
292 $calloutlist[$macroname] = $methodref;
294 $this->_macro_callouts = $calloutlist;
302 $this->_blockdef_handler = $handler;
305 #---------------------------[trigger callouts]-------------------------------------#
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);
320 $node->unknown = TRUE;
321 $node->linkparts->symlinkpath = $node->linkparts->symlink .
':';
332 if (isset($this->_rawlink_handler))
333 $node = call_user_func($this->_rawlink_handler,$node);
335 $node->unknown = TRUE;
336 $node->linkparts->rawlinkaddress = $node->linkparts->rawlink;
348 if (isset($callbacks[$node->macroname]))
350 $node = call_user_func($callbacks[$node->macroname],$node);
351 $node->processed = TRUE;
361 $callbacks = isset($this->_class_callouts[$node->type])?$this->_class_callouts[$node->type]:NULL;
362 if (!empty($callbacks))
364 $classes = !empty($node->decoration->classes)?$node->decoration->classes:NULL;
365 if (!empty($classes))
367 foreach ($classes as $class)
369 $callback = isset($callbacks[$class])?$callbacks[$class]:NULL;
372 $node = call_user_func($callback,$node);
386 $callbacks = isset($this->_property_callouts[$node->type])?$this->_property_callouts[$node->type]:NULL;
387 if (!empty($callbacks))
389 $properties = !empty($node->decoration->properties)?$node->decoration->properties:NULL;
390 if (!empty($properties))
392 foreach ($properties as $propertyindex => $property)
394 $callback = isset($callbacks[$propertyindex])?$callbacks[$propertyindex]:NULL;
397 $node = call_user_func($callback,$node);
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);
421 #--------------------------------[ utilities ]---------------------------------------#
437 if (!empty($node->linkparts->symlink)) {
439 $node->decoration->attributes[
'src'] =
440 $node->linkparts->symlinkpath . $node->linkparts->internalselector;
441 $node->decoration->attributedelimiters[
'src'] =
'"';
443 # external address for src
445 $node->decoration->attributes[
'src'] =
446 !empty($node->linkparts->externaladdress)?$node->linkparts->externaladdress:NULL;
447 $node->decoration->attributedelimiters[
'src'] =
'"';
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'] =
'"';
456 elseif ($node->target)
458 $node->decoration->attributes[
'alt'] = substr($this->
char_filter(strip_tags($node->target),$node),0,60) .
'...';
459 $node->decoration->attributedelimiters[
'alt'] =
'"';
462 if (!empty($node->title)) {
463 $node->decoration->attributes[
'title'] = $node->title;
464 $node->decoration->attributedelimiters[
'title'] =
'"';
466 # standard prepare node...
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] =
'"';
487 $node->linkparts->symlink =
'Anchor';
489 $node->decoration->attributes[$attributename] = $node->linkparts->symlinkpath . $node->linkparts->anchor;
490 $node->decoration->attributedelimiters[$attributename] =
'"';
493 elseif (!empty($node->linkparts->symlink)) {
495 $node->decoration->attributes[$attributename] =
496 $node->linkparts->symlinkpath . (!empty($node->linkparts->internalselector)?$node->linkparts->internalselector:
'');
497 $node->decoration->attributedelimiters[$attributename] =
'"';
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] =
'"';
506 $node->decoration->attributes[$attributename] =
507 !empty($node->linkparts->rawlinkaddress)?$node->linkparts->rawlinkaddress:NULL;
508 $node->decoration->attributedelimiters[$attributename] =
'"';
510 if (!empty($node->title)) {
511 $node->decoration->attributes[
'title'] = $node->title;
512 $node->decoration->attributedelimiters[
'title'] =
'"';
525 if (empty($node->output) and ($node->caption)) {
526 $node->output = $node->caption;
542 # convert input decoration attributes, values, and properties into html attributes
543 $attributes = array();
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];
552 $values = !empty($node->decoration->classes)?$node->decoration->classes:array();
553 $classes = preg_replace(
'/"/',
'\\"',implode(
' ',$values));
554 if (!empty($classes)) $attributes[]=
'class="' . $classes .
'"';
556 $properties = array();
557 $properties = !empty($node->decoration->properties)?$node->decoration->properties:array();
559 foreach ($properties as $key => $value) {
560 $styles[] = $key .
':' . $value;
562 if (!empty($styles)) {
563 $style = implode(
';',preg_replace(
'/"/',
'\\"',$styles));
564 $attributes[] =
'style="' . $style .
'"';
566 if (!empty($attributes)) $node->opentag_head .=
' ';
567 $node->attributes = $attributes;
578 return $node->opentag_head . implode(
' ',$node->attributes) . $node->opentag_tail
579 . $node->elementcontent . $node->closetag;
586 if (isset($this->_charfilter_handler)) {
587 return call_user_func($this->_charfilter_handler,$text,$node);
589 return htmlspecialchars($text);
593 #------------------------------------------------------------------------------#
594 #-----------------------------[ node emitters ]--------------------------------#
595 #------------------------------------------------------------------------------#
596 #==============================[ document ]====================================#
605 $node->opentag_head =
'';
606 $node->closetag =
'';
609 $node = $this->
call_event(
'onafteremit',$node);
610 return $node->opentag_head . $node->elementcontent . $node->closetag;
612 #=========================[ basic processing ]=================================#
619 $node->opentag_head =
"\n<p";
620 $node->opentag_tail =
">";
622 $node->closetag =
"</p>";
632 return $this->
char_filter($node->textcontent,$node);
634 #================================[ core markup ]===============================#
635 #--------------------------------[ basic markup ]------------------------------#
642 $node->opentag_head =
"\n<h" . $node->level;
643 $node->opentag_tail =
">";
645 $node->closetag =
"</h". $node->level .
">";
663 return "<strong>" . $this->
emit_children($node) .
"</strong>";
681 #--------------------------------[ links ]-------------------------------------#
690 $address = $node->target;
692 if (preg_match($this->_link_re,$address,$matches))
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'] =
'');
707 $node->linkparts =
new StdClass;
708 $node->linkparts->anchor = $matches[
'anchor'];
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'];
715 $node->linkparts->externaladdress = $matches[
'external_address'];
716 $node->linkparts->externalprotocol = $matches[
'external_proto'];
717 $node->linkparts->externalselector = $matches[
'external_selector'];
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'];
725 $node->linkparts->rawlink = $matches[
'rawlink'];
727 if (empty($node->caption) and empty($node->linkparts->anchor)) {
728 if ($matches[
'external_mailaddress']) {
729 $node->caption = $node->linkparts->externalselector;
731 $node->caption = $node->target;
735 $node->opentag_head =
"<a";
736 $node->opentag_tail =
">";
737 $node->elementcontent = $node->caption;
738 $node->closetag =
"</a>";
739 $node->unknown = FALSE;
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;
748 #--------------------------------[ images ]------------------------------------#
757 $address = $node->target;
759 if (preg_match($this->_image_re,$address,$matches))
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'] =
'');
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'];
776 $node->linkparts->externaladdress = $matches[
'external_address'];
777 $node->linkparts->externalprotocol = $matches[
'external_proto'];
778 $node->linkparts->externalselector = $matches[
'external_selector'];
780 $node->linkparts->rawlink = $matches[
'rawlink'];
782 $node->opentag_head =
"<img";
783 $node->opentag_tail =
"/>";
785 return $node->opentag_head . implode(
' ',$node->attributes) . $node->opentag_tail;
787 #--------------------------------[ lists ]-------------------------------------#
794 $node->opentag_head =
"\n<dl";
795 $node->opentag_tail =
">";
797 $node->closetag =
"\n</dl>";
807 $node->opentag_head =
"\n<dt";
808 $node->opentag_tail =
">";
810 $node->closetag =
"</dt>";
820 $node->opentag_head =
"\n<dd";
821 $node->opentag_tail =
">";
823 $node->closetag =
"</dd>";
833 $node->opentag_head =
"\n<ol";
834 $node->opentag_tail =
">";
836 $node->closetag =
"\n</ol>";
848 $node->opentag_head =
"\n<ul";
849 $node->opentag_tail =
">";
851 $node->closetag =
"\n</ul>";
861 $node->opentag_head =
"\n<li";
862 $node->opentag_tail =
">";
864 $node->closetag =
"</li>";
868 #--------------------------------[ tables ]------------------------------------#
875 $node->opentag_head =
"\n<table";
876 $node->opentag_tail =
">";
878 $node->closetag =
"\n</table>";
888 $node->opentag_head =
"\n<tr";
889 $node->opentag_tail =
">";
891 $node->closetag =
"\n</tr>";
901 $node->opentag_head =
"\n<th";
902 $node->opentag_tail =
">\n";
904 $node->closetag =
"</th>";
914 $node->opentag_head =
"\n<td";
915 $node->opentag_tail =
">\n";
917 $node->closetag =
"</td>";
921 #=========================[ special decorators ]===============================#
922 #---------------------------[ span decoration ]--------------------------------#
929 $node->opentag_head =
"<span";
930 $node->opentag_tail =
">";
932 $node->closetag =
"</span>";
936 #----------------------------[ block dividers ]--------------------------------#
943 $node->opentag_head =
"\n<div";
944 $node->opentag_tail =
">";
945 $node->elementcontent =
'';
946 $node->closetag =
"\n</div>";
950 #============================[ preformatted text ]=============================#
957 $node->opentag_head =
"<code";
958 $node->opentag_tail =
">";
959 $node->elementcontent = $node->textcontent;
960 $node->escapecontent = TRUE;
961 $node->closetag =
"</code>";
963 if ($node->escapecontent) $node->elementcontent = $this->
char_filter($node->elementcontent,$node);
972 $node->opentag_head =
"\n<pre";
973 $node->opentag_tail =
">\n";
974 $node->elementcontent = $node->textcontent;
975 $node->escapecontent = TRUE;
976 $node->closetag =
"</pre>";
978 if ($node->escapecontent) $node->elementcontent = $this->
char_filter($node->elementcontent,$node);
982 #==============================[ advanced markup ]=============================#
983 #------------------------------[ block declarations ]----------------------------#
990 $blocktag = $node->blocktag;
991 $node->knowntag = in_array($blocktag,$this->_blocktags);
992 if (!$node->knowntag)
994 $node->opentag_head =
"\n(:$blocktag " . $node->decoration->markup;
995 $node->opentag_tail =
":)";
996 $node->closetag =
"\n(:{$blocktag}end:)";
998 elseif (substr($blocktag,0,2) ==
'dl')
1000 $dlmltag = substr($blocktag,2);
1001 $node->opentag_head =
"\n<dl:" . $dlmltag;
1002 $node->opentag_tail =
">";
1003 $node->closetag =
"\n</dl:" . $dlmltag.
'>';
1007 $node->opentag_head =
"\n<$blocktag";
1008 $node->opentag_tail =
">";
1009 $node->closetag =
"\n</$blocktag>";
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)
1022 return $node->opentag_head . $node->opentag_tail
1023 . $node->elementcontent . $node->closetag;
1026 #--------------------------------[ macros ]--------------------------------#
1034 $node->processed = FALSE;
1036 if ($node->processed)
1038 return $node->output;
1040 $opentag_head =
'<<' . $node->macroname;
1041 $arguments = property_exists($node,
"decoration")? $node->decoration->markup:
'';
1042 if ($arguments !=
'') $arguments =
' ' . $arguments;
1043 $caption = $node->caption;
1045 $caption =
'|' . $caption;
1047 return $this->
char_filter($opentag_head . $arguments . $caption . $closetag,$node);