*/ /** Convert the Markdown text to html format */ class markdown { /** To debug the markdown analyzer, activate the option */ public $debug = false; /** The list of the HTML elements used by block */ private $blockid = array ("
"; // Titre2 // ------ $search[] = "/(.+)\\n--+$/Um"; $replace[] = "
"; // SEPARATORS : *** --- ___ * * * - - - _ _ _ // Must be placed before EMPHASIS $search[] = "/^[*_-] ?[*_-] ?[*_-]$/Um"; $replace[] = "
"; $markdown = preg_replace ($search, $replace, $markdown); $textArray = explode ("\n", $markdown); $pos = 0; $html = $this->detectBlock ($textArray, 0, $pos); $html = str_replace ("
", "", $html); $html = str_replace ("", "", $html); $html = trim ($html); return $html; } /** Search and replace in the paragraph on one line * @param string $line The line to analyze */ private function searchReplace ($line) { if ($this->debug) echo "CALL searchReplace ($line)\n"; // REMEMBER : THE $line is already in HTML ENTITIES ! // Quotes : " $res = $line; // Manage the
\\1";
foreach ($search as $key=>$pattern)
{
$start = 0;
while (1)
{
$start = strpos ($res, $pattern, $start);
if ($start === false)
break;
$end = strpos ($res, $pattern, $start + strlen ($pattern));
if ($end === false)
break;
if ($res[$start+1] === $pattern)
{
// Pattern too long, not this test : skip it
$start += strlen ($pattern) + strspn ($res, $pattern, $start+1);
continue;
}
if ($start > 1 && $res[$start-1] === "\\")
{
// Search the ending pattern to skip it. Remove the backslash
$res = substr ($res, 0, $start - 1) . substr ($res, $start);
}
else
{
// It is the real pattern found, without backslash. Replace by the
// $replace value
$content = substr ($res, $start + strlen ($pattern),
$end - $start - strlen ($pattern));
if (trim ($content) !== "")
{
$first = substr ($replace[$key], 0, strpos ($replace[$key], "\\1"));
$second = substr ($replace[$key], strpos ($replace[$key], "\\1")+2);
$res = substr ($res, 0, $start).$first.$content.$second.
substr ($res, $end + strlen ($pattern));
}
}
$start = $end + strlen ($pattern);
}
}
// Manage the others cases
$search = array ();
$replace = array ();
// Titles short
// == TITRE1
$search[] = '~^([^\\\\]|^)(==+ (.+)( ==+)?)$~Um';
$replace[] = ''."\n".''; // -- TITRE2 $search[] = '~^([^\\\\]|^)(--+ (.+)( --+)?)$~Um'; $replace[] = '
\n';
// LINKS (can be relative)
// images
$search[] = '~([^\\\\]|^)(!\[(.+)\]\((.+)\))~';
$replace[] = '\1';
// [Google Site](http://google.fr/ "With help bubble")
$search[] = '~([^\\\\!]|^)(\[(.+)\]\((.+) "(.+)"\))~';
$replace[] = '\1\3';
// [Google Site](http://google.fr/)
$search[] = '~([^\\\\!]|^)(\[(.+)\]\((.+)\))~U';
$replace[] = '\1\3';
// Automatics links :
//
'; // ##### Title5 $search[] = '~^([^\\\\]|^)?(##### (.+)( +#+)?)$~Um'; $replace[] = '
'; // #### Title4 $search[] = '~^([^\\\\]|^)?(#### (.+)( +#+)?)$~Um'; $replace[] = '
'; // ### Title3 $search[] = '~^([^\\\\]|^)?(### (.+)( +#+)?)$~Um'; $replace[] = '
'; // ## Title2 $search[] = '~^([^\\\\]|^)?(## (.+)( +#+)?)$~Um'; $replace[] = '
'; // # Title1 $search[] = '~^([^\\\\]|^)?(# (.+)( +#+)?)$~Um'; $replace[] = '
'; // Remove the backslashes on the existing regex foreach ($search as $s) { $s = str_replace ('([^\\\\]|^)?', '([\\\\])', $s); $s = str_replace ('([^\\\\]|^)', '([\\\\])', $s); $s = str_replace ('([^\\\\!]|^)', '([\\\\])', $s); $s = str_replace ('([^\\\\*]|^)', '([\\\\])', $s); $s = str_replace ('([^\\\\_]|^)', '([\\\\])', $s); $search[] = $s; $replace[] = '\2'; } /*foreach ($search as $key=>$s) { echo "$key => $s\n"; $res = preg_replace ($s, $replace[$key], $res); echo "$res\n"; }*/ $res = preg_replace ($search, $replace, $res); return $res; } /** Return HTML code corresponding to the code block * @param array $text The Markdown text to translate split by \n * @param integer $depth The depth of current bloc (in number of space) * @param integer &$pos The start line number of the bloc */ private function typeCode ($text, $depth, &$pos) { if ($this->debug) echo "CALL typeCode (\$text, $depth, $pos)\n"; $posStart = $pos; $content = ""; // End of code block : end of markdown text / depth lighter than $depth while (isset ($text[$pos]) && $this->depth($text[$pos]) >= $depth) { // The Code blocks can't be imbricated if ($pos > $posStart) $content .= "\n"; $content .= substr ($text[$pos], $depth); $pos++; } // Insert Geshi on $content if ($this->debug) echo "RETURN typeCode :
$content\n";
return "$content\n";
}
/** Return HTML code corresponding to the OL block
* @param array $text The Markdown text to translate split by \n
* @param integer $depth The depth of current bloc (in number of space)
* @param integer &$pos The start line number of the bloc
*/
private function typeOL ($text, $depth, &$pos)
{
if ($this->debug) echo "CALL typeOL (\$text, $depth, $pos)\n";
$content = $this->typeOLUL ($text, $depth, $pos, "ol");
if ($this->debug) echo "RETURN typeOL : $content\n";
return $content;
}
/** Return HTML code corresponding to the UL block
* @param array $text The Markdown text to translate split by \n
* @param integer $depth The depth of current bloc (in number of space)
* @param integer &$pos The start line number of the bloc
*/
private function typeUL ($text, $depth, &$pos)
{
if ($this->debug) echo "CALL typeUL (\$text, $depth, $pos)\n";
$content = $this->typeOLUL ($text, $depth, $pos, "ul");
if ($this->debug) echo "RETURN typeUL : $content\n";
return $content;
}
/** Return the HTML code corresponding to the OL/UL block
* @param array $text The Markdown text to translate split by \n
* @param integer $depth The depth of current bloc (in number of space)
* @param integer &$pos The start line number of the bloc
* @param string $type The block type : "ul" or "ol"
*/
private function typeOLUL ($text, $depth, &$pos, $type)
{
if ($this->debug) echo "CALL typeOLUL (\$text, $depth, $pos, $type)\n";
$content = "";
// End of OL/UL block : end of markdown text / depth lighter than $depth /
// linetype changed
$blockStart = $pos;
$blockContent = "";
while (isset ($text[$pos]) &&
$this->depth($text[$pos]) >= $depth &&
$this->lineType ($text[$pos]) === $type)
{
if ($this->debug)
echo "Start while $pos\n";
if (1)
{
$content .= str_repeat (" ", ($depth+2))."$content
\n"; return "$content
\n"; } /** Detect the type of the text and call the appropriate function * * @param array $text The Markdown text to translate split by \n * @param integer $depth The depth of current bloc (in number of space) * @param integer &$pos The start line number of the bloc * @return the HTML code */ private function detectBlock ($text, $depth, &$pos) { if ($this->debug) echo "CALL detectBlock (\$text, $depth, $pos)\n"; $content = ""; $blockContent = ""; // detect the type and call the right type function while (isset ($text[$pos])) { if ($this->depth ($text[$pos]) > $depth && $depth === 0) { // New block code if ($this->debug) echo "New block code\n"; $content .= $this->typeCode ($text, $this->depth ($text[$pos]), $pos); continue; } elseif ($this->depth ($text[$pos]) > $depth) { if ($this->debug) echo "CALL DEPTH > MINDEPTH (".$this->depth ($text[$pos]). " > $depth)\n"; $content .= $this->detectBlock ($text, $this->depth ($text[$pos]), $pos); continue; } elseif ($this->depth ($text[$pos]) < $depth) { if ($this->debug) echo "CALL DEPTH > MINDEPTH (".$this->depth ($text[$pos]). " < $depth)\n"; return $content; } $type = $this->lineType ($text[$pos]); $func = "type$type"; if ($this->debug) echo "FROM DETECT : CALL $func (line=".$text[$pos].")\n"; $content .= str_repeat (" ", $depth). $this->$func ($text, $depth, $pos); } return $content; } /** Return the Type of object in the provided line * p, ul, ol, code * @param string $line The line to get the type */ private function lineType ($line) { if (! isset ($line{0})) return "NONE"; if (preg_match ("/^[ \t]*[+*-] /", $line) === 1) return "ul"; if (preg_match ("/^[ \t]*[0-9]+\. /", $line) === 1) return "ol"; if (preg_match ("/^( |\t)+/", $line) === 1) return "code"; return "p"; } /** Return the depth of the provided line * @param string $line Line to analyze * @return the depth of the line */ private function depth ($line) { return strspn ($line, " "); } }