<?php class DText { static public function parse($str) { $state = ['newline']; $result = ''; # Normalize newlines. $str = trim($str); $str = preg_replace([ '/(\r\n?)/', '/\n{3,}/', # Nuke spaces between newlines. '/ *\n */', ], [ "\n", "\n\n", "\n", ], $str); $str = htmlentities($str); # Keep newline, use carriage return for split. $str = str_replace("\n", "\n\r", $str); $data = explode("\r", $str); # Parse header and list first, line by line. foreach ($data as $d) { $result .= self::parseline($d, $state); } # Parse inline tags as a whole. $result = self::parseinline($result); # htmLawed ensures valid html output. require_once Rails::root() . '/vendor/htmLawed/htmLawed.php'; return htmLawed($result); } static public function parseinline($str) { # Short links subtitution: $str = preg_replace_callback('/\[\[(.+?)\]\]/', function($m) { # [[title]] or [[title|label]] ;link to wiki $data = explode('|', $m[1], 2); $title = $data[0]; $label = isset($data[1]) ? $data[1] : $title; return "<a href=\"/wiki/show?title=" . urlencode(html_entity_decode(str_replace(" ", "_", $title))) . "\">" . $label . "</a>"; }, $str); $str = preg_replace_callback('/\{\{(.+?)\}\}/', function($m) { # {{post tags here}} ;search post with tags return "<a href=\"/post?tags=" . urlencode(html_entity_decode($m[1])) . "\">" . $m[1] . "</a>"; }, $str); # Miscellaneous single line tags subtitution. $str = preg_replace([ '/\[b\](.+?)\[\/b\]/', '/\[i\](.+?)\[\/i\]/', '/(post #(\d+))/i', '/(forum #(\d+))/i', '/(comment #(\d+))/i', '/(pool #(\d+))/i', # Single line spoiler tags. '/\[spoilers?\](.+?)\[\/spoilers?\]/', '/\[spoilers?=(.+?)\](.+?)\[\/spoilers?\]/', # Multi line spoiler tags. '/\[spoilers?\]/', '/\[spoilers?=(.+?)\]/', '/\[\/spoilers?\]/', # Quote. '/\[quote\]/', '/\[\/quote\]/', ], [ '<strong>\1</strong>', '<em>\1</em>', '<a href="/post/show/\2">\1</a>', '<a href="/forum/show/\2">\1</a>', '<a href="/comment/show/\2">\1</a>', '<a href="/pool/show/\2">\1</a>', '<span class="spoiler" onclick="Comment.spoiler(this); return false;"><span class="spoilerwarning">spoiler</span></span><span class="spoilertext" style="display: none">\1</span>', '<span class="spoiler" onclick="Comment.spoiler(this); return false;"><span class="spoilerwarning">\1</span></span><span class="spoilertext" style="display: none">\2</span>', '<span class="spoiler" onclick="Comment.spoiler(this); return false;"><span class="spoilerwarning">spoiler</span></span><div class="spoilertext" style="display: none">', '<span class="spoiler" onclick="Comment.spoiler(this); return false;"><span class="spoilerwarning">\1</span></span><div class="spoilertext" style="display: none">', '</div>', '<blockquote><div>', '</div></blockquote>', ], $str); $str = self::parseurl($str); return preg_replace([ # Extraneous newlines before closing div are unnecessary. '/\n+(<\/div>)/', # So are after headers, lists, and blockquotes. '/(<\/(ul|h\d+|blockquote)>)\n+/', # And after opening blockquote. '/(<blockquote><div>)\n+/', '/\n/', ], [ '\1', '\1', '\1', '<br>', ], $str); } static public function parseline($str, &$state) { if (preg_match('/\d/', end($state)) || preg_match('/^\*+\s+/', $str)) { return self::parselist($str, $state); } elseif (preg_match('/^(h[1-6])\.\s*(.+)\n*/', $str, $m)) { return "<${m[1]}>${m[2]}</${m[1]}>"; } else { return $str; } } static public function parselist($str, &$state) { $html = ''; if (!preg_match('/\d/', end($state))) { $state[] = "1"; $html .= '<ul>'; } else { $n = substr_count((preg_match('/^\*+\s+/', $str, $m) ? $m[0] : ''), '*'); $last = (int)end($state); if ($n < $last) { $html .= str_repeat('</ul>', $last - $n); array_pop($state); $state[] = (string)$n; } elseif ($n > $last) { $html .= '<ul>'; array_pop($state); $state[] = (string)($last + 1); } if (!preg_match('/^\*+\s+/', $str)) { array_pop($state); return $html . self::parseline($str, $state); } } $html .= preg_replace('/\*+\s+(.+)\n*/', '<li>\1', $str); return $html; } static public function parseurl($str) { # Basic URL pattern $url = '(h?ttps?:\/\/\[?(:{0,2}[\w\-]+)((:{1,2}|\.)[\w\-]+)*\]?(:\d+)*(\/[^\s\n<]*)*)'; # Substitute url tag in this form: return preg_replace([ '/(^|[\s\(>])' . $url . '/', # url '/<<\s*' . $url . '\s*\|\s*(.+?)\s*>>/', # <<url|label>> '/(^|[\s>])"(.+?)":' . $url . '/', # "label":url '/<<\s*' . $url . '\s*>>/', # <<url>> '/<a href="ttp/', # Fix ttp(s) scheme ], [ '\1<a href="\2">\2</a>', '<a href="\1">\7</a>', '\1<a href="\3">\2</a>', '<a href="\1">\1</a>', '<a href="http', ], $str); } }