2013-10-27 01:06:58 +02:00
|
|
|
<?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);
|
|
|
|
|
2013-11-08 17:37:29 +01:00
|
|
|
# htmLawed ensures valid html output.
|
|
|
|
require_once Rails::root() . '/vendor/htmLawed/htmLawed.php';
|
|
|
|
return htmLawed($result);
|
2013-10-27 01:06:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2013-11-08 17:37:29 +01:00
|
|
|
$n = substr_count((preg_match('/^\*+\s+/', $str, $m) ? $m[0] : ''), '*');
|
2013-10-27 01:06:58 +02:00
|
|
|
$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);
|
2013-11-08 17:37:29 +01:00
|
|
|
$state[] = (string)($last + 1);
|
2013-10-27 01:06:58 +02:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|