Uses the same license as pmwiki. I live mostly offline.. Version: 0.5, october 4th, 2005 Some description.. Adds: (:cut name:) .. (:cutend:), (:copy name [sourceclip]:) .. (:copyend:) and (:paste name:) .. (:pasteend:) markups. The (:xxxend:) can take the name parameter to match: (:xxxend clipname:) allowing nesting. clipname is lowercase or {$Targets}; (:copy:) and (:paste:) can take additional (processed in this order): * sourceclip parameter => gets clip from another page or clip * extract=regular expression => extracts parts denominated in regexpr * id=name => extracts lines beginning with 'name:' ; sections beginning with '! id' ; tables beginning with '||id' * lines=3..5 parameter => takes lines 3..5 only * 'sort' and 'reverse' parameter (sort only works on textual level: confused by markup or HTML) * list=bullet, list=enumerated => output list in some format * columns=4 parameter => output table Adds: (:for name1,name2,name3:) text with (:item:) substitution (:end:) no substitution (:begin:) again with (:forend:) Adds: (:foreach sourceclip:) text with (:item:) substitution (:end:) no substitution (:begin:) again with (:forend:) both do line-based sustitutions: all (:item:) is replaced with the line, all (:index:) with the index number sourceclip as above, taking the same parameters Clips are stored in $XL, so XLPages can define resources: 'clip-clipname' => 'clipcontent'. So you don't have to use the (:if:) syntax when a clip isn't available, just prepare a default. Skin or other templates can access clips directly through the $[..] syntax. Tip: use (:nl:) to insert linebreaks. It is even possible to redefine skin-elements that are $[..]. But beware of opening up vulnerabilities! */ $ClipNamePattern='(?:[a-z]+|[A-Z][A-Za-z0-9_.]+(?:[#][a-z]+)?)'; Markup('cut','$v) if(!strncmp($k,'clip-',5)) $XL['clip'][$k]=htmlspecialchars($v); function MorphedClip($clip,$parms){ global $XL; if(!$clip and !$parms) {# template filling $c=$XL['clip']["clip-"]; $r=substr($c,0,strpos_($c,"\n")); $XL['clip']["clip-"]=substr($c,strlen($r)+1); return $r; } $clip=GetClip($clip); if(!$parms) return $clip; if(preg_match("/(?:extract=| )?(([!@#%^\\/]).*?\\2[a-z]*)/s",$parms,$m)) $parms=str_replace($m[0],' ',$parms); $parms=ParseArgs($parms); if($b=$parms['id']) { preg_match("/^[:]?[ ]*{$b}[ ]*[:\n][ ]*(.*?)[ ]*$|^[|][|][ ]*{$b}[ ]*[|][|][ ]*(.*?)[ |]*$|!+[ ]*{$b}[ ]*\n[ ]*(.*)[ ]*(?:\n!|(?-m)$)/mi",$clip,$im); $clip=@$im[1]; } $clip=explode("\n",$clip); if($b=@$parms['lines']) { list($b,$e)=explode('..',$b); if(!$e) { $e=$b; $b=0; } $clip=array_slice($clip,$b,$e-$b); } if($m[1]) $clip=MatchList($m[1],$clip); foreach((array)$parms[''] as $p) { if($p=='sort') sort($clip); # doesn't function on links unless written [[Tag -> Link]] format!! Better yet, links could carry a sort info parameter elseif($p=='reverse') $clip=array_reverse($clip); } if($b=$parms['list']) { $e=array('bullet'=>'*','enum'=>'#','enumerated'=>'#'); if(!$e=$e[$b]) $e=$b; $clip=array_map(create_function('$c',"return '$e '.\$c;"),$clip); } if(!$c=$parms['columns']) return implode("\n",$clip); return FmtAsTable($clip,$c,'width=100%'); } function GetClip($clip) { global $XL,$pagename,$GroupAttributesFmt; $GLOBALS['RedoMarkupLine']++; list($pg,$clip)=explode('#',$clip); if(!$clip and !trim($pg,'a..z')) return $XL['clip']["clip-$pg"]; # remainder gets a clip from another page, much like (:include:) if(count($pg=explode('.',$pg))==1) array_unshift($pg,substr($pagename,0,strpos($pagename,'.'))); $page=ReadPage("$pg[0].$pg[1]", READPAGE_CURRENT); #!# if(!$clp=rtrim(" $clip")) return $page['text']; $pat="/[(]:(copy|cut)$clp:[)]\\s*\n?(.+?)\n?[(]:\\1end(?: $clip)?:[)]/sme"; if(preg_match($pat,$page['text'],$m)) return @$m[2]; return @$XL['clip']["clip-$clip"]; # there may be a default set in XLPage } function FmtAsTable($list,$columns=1,$tattr='') { if(!$n = floor((count($list)+$columns-1) / $columns)) # number per column return "
...
"; $list=array_chunk($list,$n); foreach(range(0,$n) as $c) { $r=''; foreach($list as $l) { $d=$l[$c]; $r.="$d"; } $t.=" $r\n"; } return "\n$t\n
"; } function MatchList($pat,$txt) { if(!$pat) return $txt; $z=array(); foreach((array) $txt as $t) if(preg_match_all($pat,$t,$m,PREG_PATTERN_ORDER)) { unset($m[0]); foreach($m as $p) { array_splice($z,count($z),0,$p); } } return $z; } function strpos_($h,$n) { return (false!==$i=strpos($h,$n)) ? $i : strlen($h); } #=============================================================== (:for:) extension # template generation (:for clip do clip:) Markup('for','>if' ,"/[(]:for ([a-zA-Z0-9]+(?:,[-a-zA-Z_0-9]+)*|each $ClipNamePattern||[{][$].*?[}]) +do +($ClipNamePattern):[)]/sme" ,"ForExpand('$1','$2')"); # debugging aid: (:viewfor:) Markup('viewfor','".str_replace("\n","\n ",(htmlspecialchars(PSS(\'$1$2\'))))'); function ForExpand($l,$txt) { global $PCache,$pagename; $GLOBALS['RedoMarkupLine']++; if(!is_array($l)) { if(!strncmp('each ',$l,5)) $l=explode("\n",GetClip(substr($l,5))); else { if($l=='{$Targets}') $l=$PCache[$pagename]['targets']; $l=explode(',',$l); } } $txt=GetClip($txt); if(substr($txt,-9)=='<:vspace>') $txt=substr($txt,0,-9); $limit=array(); $txt=preg_replace("/[(]:begin(?: max=([0-9]+))?:[)]/e","'(:begin:)'.PZZ(\$limit[]=0$1)",$txt); $txt=preg_split("/(^ +)?[(]:begin:[)]( *\n)?/sm",$txt,PREG_SPLIT_DELIM_CAPTURE); $r=''; if((count($txt) > 1) and false===strpos($txt[0],'(:end:)')) $r=array_shift($txt); foreach($txt as $n=>$t) { $t=preg_split("/(^[ ]+)?[(]:end:[)]([ ]*\n)?/sm",$t); $x=array_shift($t); foreach($l as $k=>$i) { $r.=str_replace(array('(:item:)','(:index:)'),array($i,$k+1),$x); } $r.=implode($t); } return $r; } ?>