WeakWiki


lib\parser.library.php

WeakWiki (WeakWiki parser library: parser.library.php) Copyright (C) 2010 Alexander Lang This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . Project Home: http://weakwiki.robnet.wmweb.at/ Contact: robbiblubber@robnet.wmweb.at



/********************************************************************************
WeakWiki (WeakWiki parser library: parser.library.php)
Copyright (C) 2010 Alexander Lang

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, see .

Project Home: http://weakwiki.robnet.wmweb.at/
Contact:      robbiblubber@robnet.wmweb.at
********************************************************************************/




require_once('./config/default.config.php');


/** Special characters. These may be escaped using "~".							*/
define('_WIKI_SPECIAL_CHARACTERS', "=~+-*#/\\[]{}&.,;_'^");

/** Depth for lists.															*/
define('_LIST_MAX_DEPTH', 6);

/** Depth for headings.															*/
define('_HEADINGS_MAX_DEPTH', 6);


/** Counter for mask symbols.													*/
$GLOBALS['_m'] = 0;


/** Parses wiki markup text
	@param $input		Text.
	@return		Parsed text.													*/
function wikiParse($input)
{
	$masked = Array();								// masked area buffer
	
	foreach($GLOBALS['mod_load'] as $mod)
	{
		if($mod->getParseMode() & MOD_MODE_BEFORE_PARSE == MOD_MODE_BEFORE_PARSE)
		{											// module before parser
			$input = $mod->parseBefore($input);
		}
	}
		
	$input = str_replace("\r\n", "\n", $input);		// simple line breaks do help
		
	$input = str_replace('<', '<', $input);		// mask tags in source
	$input = str_replace('>', '>', $input);
	
	
													// mask nonwiki areas
													// nonwiki tags must not have
	while(true)										// any leading or following
	{												// whitespaces
		$i = 0;
		$c = _getArea($input, "\n{{{\n", "\n}}}\n", $i);
				
		if($i < 0) break;
				
		$mask = _getMask($input);
		array_push($masked, Array("\n
" . substr($c, 5, strlen($c) - 10) . "
\n", $mask)); $input = str_replace(substr($c, 1, strlen($c) - 2), $mask, $input); } $input = str_replace("\t", " ", $input); // makes things easier // mask masked special characters for($i = 0; $i < strlen(_WIKI_SPECIAL_CHARACTERS); $i++) { // be aware that the tilde (~) // is the first character in // _WIKI_SPECIAL_CHARACTERS // on purpose; other conditions // might destroy "~~"-markups; $c = substr(_WIKI_SPECIAL_CHARACTERS, $i, 1); $mask = _getMask($input); array_push($masked, Array($c, $mask)); $input = str_replace("~" . $c, $mask, $input); } foreach($GLOBALS['mod_load'] as $mod) { if(($mod->getParseMode() & MOD_MODE_PARSE) == MOD_MODE_PARSE) { // module parser $input = $mod->parse($input); } } while(true) // mask <<@ignore>> areas. { $i = 0; $c = _getArea($input, ">>@ignore<<", ">>@/ignore<<", $i); if($i < 0) break; $mask = _getMask($input); array_push($masked, Array(substr($c, 23, strlen($c) - 47), $mask)); $input = str_replace($c, $mask, $input); } // insert automatic linebreaks if(X_AUTO_LINEBREAK) // (extended) { $rval = ""; $pre = false; foreach(explode("\n", $input) as $i) { $m = trim($i); $p = ((substr($m, 0, 1) == "|") || (substr($m, 0, 1) == "=") || (substr($m, 0, 2) == "* ") || (substr($m, 0, 2) == "# ") || (substr($m, 0, 3) == "[[[") || (substr($m, 0, 3) == "]]]") || (substr($m, 0, 4) == "----") || (substr($m, 0, 12) == ">>>") || (substr($m, 0, 12) == "<<<")); if(strpos($m, "
			if(strpos($m, "
") !== false) { $pre = true; } 
			
			$p = ($p | $pre);
			$rval .= $i . ($p ? "\n" : "
\n"); if(strpos($m, "
") !== false) { $pre = false; } } $input = $rval; } // Horizontal rule for($i = 0; $i < 2; $i++) { $input = str_replace("\n----\n", "\n
\n", $input); } // special boxes $input = _parseBraced($input, "[[[", "]]]", "
\n", "\n
", Array(), "_table_flags"); // format classes $input = _parseBraced($input, "<<@f:", "<<@/f>>", "", "", Array(), '_format_class'); // html-support $input = _parseBraced($input, "<<@html>>", "<<@/html>>", "", "", Array(), '_format_html'); $input = _parseList($input, '*', 'ul'); // simple lists $input = _parseList($input, '#', 'ol'); // ordered lists $mask = _getMask($input); // mask protocol headings $input = str_replace("://", $mask, $input); // italics $input = _parseBraced($input, "//", "//", "", ""); $input = str_replace($mask, "://", $input); // unmask protocol headings // bolds $input = _parseBraced($input, "**", "**", "", ""); // inset $input = str_replace('<--', "", str_replace('-->', "
", $input)); // alignment $input = str_replace('<<<', "
", str_replace('>>>', "
", $input)); $input = str_replace('>>@right<<', "
", str_replace('>>@/right<<', "
", $input)); $input = str_replace('>>@center<<', "
", str_replace('>>@/center<<', "
", $input)); $input = str_replace('>>@left<<', "
", str_replace('>>@/left<<', "
", $input)); if(X_MARKUPS) { // strike $input = _parseBraced($input, "--", "--", "", ""); // underline $input = _parseBraced($input, "__", "__", "", ""); // quotes $input = _parseBraced($input, "'''", "'''", "
", "
"); // superscript $input = _parseBraced($input, "^^", "^^", "", ""); // subscript $input = _parseBraced($input, ",,", ",,", "", ""); } $input = _parseHeadings($input); // Headers and TOC $n = count($masked); $i = 0; // mask image links while(true) { $c = _getArea($input, "{{", "}}", $i); if($i < 0) break; $mask = _getMask($input); array_push($masked, Array($c, $mask)); } for($i = $n; $i < count($masked); $i++) { $input = str_replace($masked[$i][0], $masked[$i][1], $input); } $n2 = $n; $n = count($masked); $i = 0; // mask explicit links while(true) { $c = _getArea($input, "[[", "]]", $i); if($i < 0) break; $mask = _getMask($input); array_push($masked, Array($c, $mask)); } for($i = $n; $i < count($masked); $i++) { $input = str_replace($masked[$i][0], $masked[$i][1], $input); } $input = _parseRawLinks($input); // parse raw links $input = _parseTables($input); // parse tables // Forced line breaks $input = str_replace("\\\\", "
\n", $input); // divs $input = str_replace(">>>", "
", $input); $input = str_replace("<<<", "
", $input); // remove masks, parsing for($j = count($masked); $j > 0; $j--) // links and images { $i = $masked[$j - 1]; if(substr($i[0], 0, 2) == "[[") { $l = trim($i[0], "[]"); $t = $l; $target = ""; $color = ""; if(strpos($l, "|") !== false) { $t = substr($l, strpos($l, "|") + 1); $l = substr($l, 0, strpos($l, "|")); } if(substr($l, 0, 3) == "@H:") { $m = explode("/", substr($l, 3)); $l = _WIKI_PATH . $m[0] . "&fileid=" . $m[1] . "&action=showhistory"; if(X_LINKS_COLOR) { $color = "class=\"history_link\""; } } else { $lp = $l; foreach($GLOBALS['mod_load'] as $mod) { if(($mod->getParseMode() & MOD_MODE_PARSE_LINKS) == MOD_MODE_PARSE_LINKS) { // module before parser $lp = $mod->parseLink($l); if($lp != $l) break; } } switch($l) // special commands { case '@side:collapse': $lp = trim(_WIKI_COMMAND . '?action=collapse&' . str_replace('action=', 'refl=', str_replace('?', '&', $_SERVER['QUERY_STRING'])), '&'); break; case '@side:expand': $lp = trim(_WIKI_COMMAND . '?action=expand&' . str_replace('action=', 'refl=', str_replace('?', '&', $_SERVER['QUERY_STRING'])), '&'); break; } if($lp != $l) { $l = $lp; $color = " class=\"functional_link\""; } else if((strpos($l, "/") === false) && (strpos($l, ":") === false)) { $l = _WIKI_PATH . $l; if(X_LINKS_COLOR) { $color = " class=\"internal_link\""; } } else { if(X_LINKS_NEWTAB) { $target = " target=\"_blank\""; } if(X_LINKS_COLOR) { $color = " class=\"external_link\""; } } } $i[0] = "" . $t . ""; } else if(substr($i[0], 0, 2) == "{{") { $l = trim($i[0], "{}"); $t = ""; if(strpos($l, "|") !== false) { $t = substr($l, strpos($l, "|") + 1); $l = substr($l, 0, strpos($l, "|")); } if(strpos($l, "/") === false) { $l = _PATH_IMAGES . $l; } $i[0] = " (($t == "") ? "" : " alt=\"" . $t . "\"") . " border=\"0\"/>"; } $input = str_replace($i[1], $i[0], $input); } foreach($GLOBALS['mod_load'] as $mod) { if($mod->getParseMode() & MOD_MODE_AFTER_PARSE == MOD_MODE_AFTER_PARSE) { // module before parser $input = $mod->parseAfter($input); } } return $input; } /** Returns a unique mask string not contained in the input string. @param $input Input text. @return Mask string. */ function _getMask($input) { $mask = $GLOBALS['_m']; while(strpos($input, ('<#' . $mask . '/>')) !== false) { $mask++; } $GLOBALS['_m'] = $mask + 1; return ('<#' . $mask . '/>'); } /** Returns the text area between markup-tags given. @param $input Input text. @param $start Start tag. @param $end End tag. @param $pos Takes the position within the text to start from. _getArea() will also write the end position of the area found in $pos. If no area could be found, $pos will be set to -1. @return Returns the markup text of the found area. */ function _getArea($input, $start, $end, &$pos = 0) { $n = strpos($input, $start, $pos); if($n === false) { // no match found $pos = -1; return ""; } $pos = strpos($input, $end, $n); return substr($input, $n, $pos + strlen($end) - $n); } /** Parses ordered and unordered lists. @param $input Input text. @param $symbol Wiki markup tag. @param $tag HTML tag. @return Returns the input text with all lists parsed. */ function _parseList($input, $symbol, $tag) { $lines = explode("\n", $input); $run = false; $hit = false; // left unchanged from version // 0.4 $rval = ""; for($i = 0; $i < count($lines); $i++) { // process lines if(substr(trim($lines[$i]), 0, strlen($symbol) + 1) == ($symbol . ' ')) { if(!$run) { $run = true; $hit = true; $lines[$i] = '<' . $tag . '>
  • ' . substr(trim($lines[$i]), strlen($symbol) + 1); } else { $lines[$i] = '
  • ' . substr(trim($lines[$i]), strlen($symbol) + 1); } } else if($run) { if(substr(trim($lines[$i]), 0, 1) != substr($symbol, 0, 1)) { $run = false; $lines[$i] = '
  • \n" . $lines[$i]; } } $rval .= $lines[$i] . "\n"; } if($hit) { $rval = _parseList($rval, $symbol . substr($symbol, 0, 1), $tag); } return $rval; } /** Parses braced areas into their HTML representations. @param $input Input string. @param $start Wiki markup start tag. @param $end Wiki markup end tag. @param $starttag HTML markup start tag. @param $endtag HTML markup end tag. @param $disallow (optional) Array of disallowed symbols within the area. @return Parsed text. */ function _parseBraced($input, $start, $end, $starttag, $endtag, $disallow = Array(), $postParse = "") { $switch = true; $parse = true; $n = -1; $k = 0; while(true) { $mark = ($switch ? $start : $end); $n = strpos($input, $mark, $n + 1); if($n === false) break; if($switch) { $k = $n; } else { $m = substr($input, $k + strlen($start), $n - $k - strlen($start)); foreach($disallow as $i) { if(strpos($m, $i) !== false) { $parse = false; break; } } if($parse) { $starttagp = $starttag; if($postParse != "") { $starttagp = $postParse($starttag, $m); } $input = substr($input, 0, $k) . $starttagp . $m . $endtag . substr($input, $n + strlen($end)); } } $switch = (!$switch); } return $input; } /** Formats HTML-enabled area. @param $startTag Start tag. @param $text Text. @return Parsed start tag. */ function _format_html($startTag, &$text) { $text = str_replace("<", "<", $text); $text = str_replace(">", ">", $text); return $startTag; } /** Formats flagged area. @param $startTag Start tag. @param $text Text. @return Parsed start tag. */ function _format_class($startTag, &$text) { $n = substr($text, 0, strpos($text, ">>")); $text = substr($text, strpos($text, ">>") + 8); return str_replace("%FLAGS%", "f_" . $n, $startTag); } /** Translates table flags into CSS classes. @param $startTag Start tag. @param $text Text. @return Parsed start tag. */ function _table_flags($startTag, &$text) { $n = str_replace("
    ", "", substr($text, 0, strpos($text, "\n"))); $text = substr($text, strpos($text, "\n")); $rval = "box"; if(strpos($n, "c") !== false) { $rval = "code"; } if(strpos($n, "r") !== false) { return str_replace("%FLAGS%", $rval . " bg_red", $startTag); } if(strpos($n, "g") !== false) { return str_replace("%FLAGS%", $rval . " bg_green", $startTag); } if(strpos($n, "b") !== false) { return str_replace("%FLAGS%", $rval . " bg_blue", $startTag); } if(strpos($n, "G") !== false) { return str_replace("%FLAGS%", $rval . " bg_grey", $startTag); } if(strpos($n, "y") !== false) { return str_replace("%FLAGS%", $rval . " bg_yellow", $startTag); } if(strpos($n, "w") !== false) { return str_replace("%FLAGS%", $rval . " bg_white", $startTag); } return str_replace("%FLAGS%", $rval, $startTag); } /** Returns the position of the next stop character. @param $input Input string. @param $stop Stop character list. @param $pos Start position. @return Position of the character from the stop list next to the starting position. */ function _nextStop($input, $stop, $pos) { $rval = strlen($input); foreach($stop as $i) { $n = strpos($input, $i, $pos); if(($n !== false) && ($n < $rval)) { $rval = $n; } } return $rval; } /** Parses raw links. @param $input Input string. @return Parsed string. */ function _parseRawLinks($input) { $rawLink = $GLOBALS['_rawLink']; $stop = $GLOBALS['_whiteSpace']; array_push($stop, "<"); array_push($stop, ">"); array_push($stop, "\""); array_push($stop, "("); array_push($stop, ")"); array_push($stop, "["); array_push($stop, "]"); $links = Array(); foreach($rawLink as $i) { $pos = 0; while(true) { $n = strpos($input, ($i . '://'), $pos); if($n === false) break; $pos = _nextStop($input, $stop, $n); $l = substr($input, $n, $pos - $n); $target = (X_LINKS_NEWTAB ? " target=\"_blank\"" : ""); $format = (X_LINKS_COLOR ? " class=\"external_link\"" : ""); array_push($links, Array($l, "" . $l . "")); } } foreach($links as $i) { $input = str_replace($i[0], $i[1], $input); } return $input; } /** Parses tables @param $input Input string. @return Parsed string. */ function _parseTables($input) { $rval = ""; $t = Array(); $run = false; $m = explode("\n", $input); $tab = true; $tclass = ""; $rclass = ""; $cclass = ""; array_push($m, "\n"); foreach($m as $i) { if($run) { if(substr(trim($i), 0, 1) == "|") { array_push($t, $i); } else { $run = false; $tab = true; foreach($t as $j) { $tag = "td"; $rclass = ""; if(substr(trim($j), 0, 2) == "|=") { $tag = "th"; $j = str_replace("|=", "|", $j); } $line = ""; foreach(explode("|", substr($j, 1, strlen($j) - 2)) as $k) { $cclass = ""; _getCellFlags($k, $tclass, $rclass, $cclass); if($cclass == "") { $cclass = $rclass; } if($cclass == "") { $cclass = $tclass; } $line .= "<" . $tag . (($cclass == "") ? ">" : (" class=\"" . $cclass . "\">")) . $k . ""; } if($tab) { $rval .= "\n" : (" class=\"" . $tclass . "\">\n")); $tab = false; } if($rclass == "") { $rclass = $tclass; } $rval .= "" : (" class=\"" . $rclass . "\">")); $rval .= $line . "\n"; } $rval .= "\n"; $rval .= $i; $t = Array(); } } else { if(substr(trim($i), 0, 1) == "|") { $run = true; array_push($t, $i); } else { $rval .= $i . "\n"; } } } return $rval; } /** Gets cell flags for flagging tables. @param $value Cell value. @param $tclass CSS table class. @param $rclass CSS row class. @param $cclass CSS cell class. */ function _getCellFlags(&$value, &$tclass, &$rclass, &$cclass) { if(strpos($value, "@") === false) return; foreach(explode("@", substr($value, 0, strpos($value, " "))) as $i) { switch($i) { case "t0": $tclass = "invisible"; break; case "r0": $rclass = "invisible"; break; case "c0": $cclass = "invisible"; break; default: switch(substr($i, 0, 2)) { case "t:": $tclass = "tf_" . substr($i, 2); break; case "r:": $rclass = "tf_" . substr($i, 2); break; case "c:": $cclass = "tf_" . substr($i, 2); break; } break; } } $value = substr($value, strpos($value, " ")); } /** Parses headings. @param $input Input string. @return Parsed string. */ function _parseHeadings($input) { $rval = ""; $n = 0; $toc = Array(); foreach(explode("\n", $input) as $i) { if(substr(trim($i), 0, 1) == "=") { $i = trim($i); $fail = true; for($j = 1; $j <= _LIST_MAX_DEPTH; $j++) { if(substr($i, 0, $j +1) == str_repeat("=", $j) . " ") { $i = trim(trim($i, "=")); $rval .= "" . $i . "\n"; array_push($toc, Array($n, $j, $i)); $n++; $fail = false; break; } } if($fail) { $rval .= $i; } } else { $rval .= $i . "\n"; } } if(strpos($rval, "<<@toc") !== false) { $toctag = substr($rval, strpos($rval, "<<@toc"), strpos($rval, ">>", strpos($rval, "<<@toc")) - strpos($rval, "<<@toc") + 8); $toctext = "
    "; if(strpos($toctag, "|") !== false) { $toctext .= "" . substr($toctag, strpos($toctag, "|") + 1, strlen($toctag) - strpos($toctag, "|") - 9) . "

    "; } foreach($toc as $i) { $toctext .= str_repeat("   ", $i[1] - 2) . "" . $i[2] . "
    "; } $toctext .= "
    \n"; $rval = str_replace($toctag, $toctext, $rval); } return $rval; } ?>

    WeakWiki