@@ -0,0 +1,23 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Blockquote extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag() : string | |||
{ | |||
return '</div>'; | |||
} | |||
public function getOpenHtmlTag() : string | |||
{ | |||
return '<div class="blockquote">'; | |||
} | |||
public function getTag(): string | |||
{ | |||
return 'blockquote'; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Bold extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</b>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<b>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'b'; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class HeadingOne extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</h1>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<h1>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'h1'; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class HeadingTwo extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</h2>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<h2>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'h2'; | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Image implements TagInterface | |||
{ | |||
public function format(TokenTag $tokenTag): string | |||
{ | |||
$link = $tokenTag->getText(); | |||
if (!preg_match('!^https?://[a-z0-9\-@:.,_&+%#?/=]+$!i', $link)) { | |||
$unknownTag = new UnknownSimpleType(); | |||
return $unknownTag->format($tokenTag); | |||
} | |||
return '<img src="' . $link . '" />'; | |||
} | |||
public function getName(): string | |||
{ | |||
return 'img'; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag): string | |||
{ | |||
return "[{$this->getName()}]{$tokenTag->getText()}[/{$this->getName()}"; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Italic extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</em>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<em>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'i'; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Link implements TagInterface | |||
{ | |||
const REG_EXP_VALID_URL = '~https?://[a-zA-Z0-9_\-.:/#?]+~'; | |||
public function format(TokenTag $tokenTag): string | |||
{ | |||
$args = $tokenTag->parseArgument(); | |||
$url = $args['href'] ?? null; | |||
if (!preg_match(self::REG_EXP_VALID_URL, $url)) { | |||
$simpleTag = new Unknown(); | |||
return $simpleTag->format($tokenTag); | |||
} | |||
return "<a href=\"{$url}\" target=\"_blank\">{$tokenTag->getText()}</a>"; | |||
} | |||
public function getName(): string | |||
{ | |||
return 'url'; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag) : string | |||
{ | |||
return "[url={$tokenTag->getArgument()}]{$tokenTag->getText()}[/url]"; | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Quote implements TagInterface | |||
{ | |||
public function format(TokenTag $tokenTag): string | |||
{ | |||
$attrs = $tokenTag->parseArgument(); | |||
return view('partial/bbcode/quote', [ | |||
'thread' => $attrs->threadId ?? 1, | |||
'page' => $attrs->threadPage ?? 1, | |||
'postId' => $attrs->postId ?? 1, | |||
'username' => $attrs->username ?? 'Unknown', | |||
'content' => $tokenTag->getText() | |||
]); | |||
} | |||
public function getName(): string | |||
{ | |||
return 'quote'; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag): string | |||
{ | |||
return "[{$this->getName()}]{$tokenTag->getText()}[/{$this->getName()}"; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Spoiler extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</span>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<span class="spoiler">'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'spoiler'; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Strikethrough extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</s>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<s>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 's'; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Text implements TagInterface | |||
{ | |||
public function format(TokenTag $tokenTag) : string | |||
{ | |||
$formattedString = "{$tokenTag->getText()}"; | |||
return $formattedString; | |||
} | |||
public function getName(): string | |||
{ | |||
return ''; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag): string | |||
{ | |||
// there is nothing special | |||
return $this->format($tokenTag); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Twitter implements TagInterface | |||
{ | |||
public function format(TokenTag $tokenTag): string | |||
{ | |||
$url = $tokenTag->getText(); | |||
return "<a href=\"{$url}\" target=\"_blank\">[Twitter Embed]</a>"; | |||
} | |||
public function getName(): string | |||
{ | |||
return 'twitter'; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag) : string | |||
{ | |||
return "[twitter]{$tokenTag->getText()}[/twitter]"; | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\SimpleTag; | |||
class Underline extends SimpleTag | |||
{ | |||
public function getCloseHtmlTag(): string | |||
{ | |||
return '</u>'; | |||
} | |||
public function getOpenHtmlTag(): string | |||
{ | |||
return '<u>'; | |||
} | |||
public function getTag() : string | |||
{ | |||
return 'u'; | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
<?php | |||
namespace App\BbCode\Tag; | |||
use Knockout\BbCode\Tag\TagInterface; | |||
use Knockout\BbCode\Tokenizer\Tag as TokenTag; | |||
class Unknown implements TagInterface | |||
{ | |||
public function format(TokenTag $tokenTag) : string | |||
{ | |||
$formattedString = "[{$tokenTag->getType()}"; | |||
if ($tokenTag->getArgument()) { | |||
$formattedString .= "={$tokenTag->getArgument()}"; | |||
} | |||
$formattedString .= "]{$tokenTag->getText()}[/{$tokenTag->getType()}]"; | |||
return $formattedString; | |||
} | |||
public function getName(): string | |||
{ | |||
return ''; | |||
} | |||
public function getOriginalText(TokenTag $tokenTag): string | |||
{ | |||
return "[{$tokenTag->getType()}]{$tokenTag->getText()}[/{$tokenTag->getType()}]"; | |||
} | |||
} |
@@ -1,118 +1,100 @@ | |||
<?php | |||
namespace App\Helper; | |||
use App\Vendor\BBCode\BBCode as Parser; | |||
//use ChrisKonnertz\BBCode\BBCode as Parser; | |||
use App\BbCode\Tag\Unknown; | |||
use App\BbCode\Tag\Text; | |||
use App\BbCode\Tag\Bold; | |||
use App\BbCode\Tag\Italic; | |||
use App\BbCode\Tag\Underline; | |||
use App\BbCode\Tag\Strikethrough; | |||
use App\BbCode\Tag\Spoiler; | |||
use App\BbCode\Tag\HeadingOne; | |||
use App\BbCode\Tag\HeadingTwo; | |||
use App\BbCode\Tag\Link; | |||
use App\BbCode\Tag\Image; | |||
use App\BbCode\Tag\Quote; | |||
use App\BbCode\Tag\Blockquote; | |||
use App\BbCode\Tag\Twitter; | |||
use Knockout\BbCode\BbCode as Parser; | |||
use Knockout\BbCode\Tokenizer\Tokenizer; | |||
class BBCode { | |||
private $parser; | |||
private $content; | |||
private $tokens; | |||
public function __construct($content) | |||
{ | |||
$this->parser = new Parser(); | |||
$this->content = $content; | |||
//$this->defineTags(); | |||
} | |||
private function defineTags() | |||
{ | |||
// bold | |||
$this->parser->addTag('b', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<strong>' : '</strong>'; | |||
}); | |||
// italics | |||
$this->parser->addTag('i', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<em>' : '</em>'; | |||
}); | |||
// underline | |||
$this->parser->addTag('u', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<u>' : '</u>'; | |||
}); | |||
// strikethrough | |||
$this->parser->addTag('s', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<s>' : '</s>'; | |||
}); | |||
// spoiler | |||
$this->parser->addTag('spoiler', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<span class="spoiler">' : '</span>'; | |||
}); | |||
// heading 1 | |||
$this->parser->addTag('h1', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<h1>' : '</h1>'; | |||
}); | |||
// heading 2 | |||
$this->parser->addTag('h2', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<h2>' : '</h2>'; | |||
}); | |||
// hyperlink | |||
$this->parser->addTag('url', function($tag, &$html, $openingTag) { | |||
if (!$openingTag) return '</a>'; | |||
return sprintf('<a href="%s">', $tag->property ?? $html); | |||
}); | |||
// blockquote | |||
$this->parser->addTag('blockquote', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<div class="blockquote">' : '</div>'; | |||
}); | |||
// code | |||
$this->parser->addTag('code', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<pre>' : '</pre>'; | |||
}); | |||
// unordered list | |||
$this->parser->addTag('ul', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<ul>' : '</ul>'; | |||
}); | |||
// ordered list | |||
$this->parser->addTag('ol', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<ol>' : '</ol>'; | |||
}); | |||
// list item | |||
$this->parser->addTag('li', function($tag, &$html, $openingTag) { | |||
return $openingTag ? '<li>' : '</li>'; | |||
}); | |||
// quote | |||
$this->parser->addTag('quote', function($tag, &$html, $openingTag) { | |||
return $this->renderQuote($tag, $html, $openingTag); | |||
}); | |||
// quote shorthand | |||
$this->parser->addTag('q', function($tag, &$html, $openingTag) { | |||
return $this->renderQuote($tag, $html, $openingTag); | |||
}); | |||
} | |||
public function renderQuote($tag, &$html, $openingTag) | |||
{ | |||
exit('kek'); | |||
if (!$openingTag) return '</div></div>'; | |||
return '<div class="quote"><a class="user" href="#">User</a><div class="postContent">'; | |||
// '<div class="quote"><a class="user" href="/thread/%d/%d#post-%d">%s</a><div class="postContent">', | |||
// $postData->threadId, | |||
// $postData->threadPage, | |||
// $postData->postId, | |||
// $postData->username | |||
$this->tokens = (new Tokenizer())->tokenize($content); | |||
} | |||
public function render() | |||
{ | |||
//$test = '[quote mentionsUser="1427" postId="659032" threadPage="1" threadId="19764" username="Dr. Magnusson"][/quote]'; | |||
return $this->parser->renderPlain($this->content); | |||
$parser = new Parser(); | |||
$unknown = new Unknown(); | |||
$parser->addTag($unknown); | |||
$text = new Text(); | |||
$parser->addTag($text); | |||
$bold = new Bold(); | |||
$parser->addTag($bold); | |||
$italic = new Italic(); | |||
$parser->addTag($italic); | |||
$underline = new Underline(); | |||
$parser->addTag($underline); | |||
$strikethrough = new Strikethrough(); | |||
$parser->addTag($strikethrough); | |||
$image = new Image(); | |||
$parser->addTag($image); | |||
$link = new Link(); | |||
$parser->addTag($link); | |||
$quote = new Quote(); | |||
$parser->addTag($quote); | |||
$blockquote = new Blockquote(); | |||
$parser->addTag($blockquote); | |||
$twitter = new Twitter(); | |||
$parser->addTag($twitter); | |||
$tags = [ | |||
$unknown, | |||
$text, | |||
$bold, | |||
$italic, | |||
$underline, | |||
$strikethrough, | |||
$image, | |||
$link, | |||
$quote, | |||
$blockquote, | |||
]; | |||
$anyChild = [ | |||
$bold, | |||
$quote, | |||
$blockquote, | |||
$text, | |||
$unknown | |||
]; | |||
foreach ($anyChild as $parent) { | |||
foreach ($tags as $tag) { | |||
$parser->addAllowedChildTag($parent, $tag); | |||
} | |||
} | |||
return $parser->format($this->tokens); | |||
} | |||
} |
@@ -23,6 +23,8 @@ class Post { | |||
$s->date = Carbon::parse($post->createdAt)->format('d/m/Y H:i'); | |||
$s->content = $post->content; | |||
//$s->content = '[quote mentionsUser="1427" postId="659032" threadPage="1" threadId="19764" username="Dr. Magnusson"][/quote]'; | |||
// grab user if available | |||
if (isset($post->user)) { | |||
$s->user = User::unwrap($post->user); | |||
@@ -139,7 +139,7 @@ class Thread { | |||
return (new Dataset($record)) | |||
->setTotalRecords($json->totalPosts) | |||
->setCurrentPage($json->currentPage) | |||
->setRecordsPerPage(40); | |||
->setRecordsPerPage(20); | |||
} | |||
public static function updateOne(int $subforumId, int $page = 1): Dataset | |||
@@ -1,333 +0,0 @@ | |||
<?php | |||
namespace App\Vendor\BBCode; | |||
use App\Vendor\BBCode\Tags\BoldTag; | |||
use App\Vendor\BBCode\Tags\NoParseTag; | |||
use App\Vendor\BBCode\Tokenizer\Token; | |||
use App\Vendor\BBCode\Tokenizer\Tokenizer; | |||
use Closure; | |||
/* | |||
* BBCode to HTML converter | |||
* | |||
* Inspired by Kai Mallea (kmallea@gmail.com) | |||
* | |||
* Licensed under the MIT license: | |||
* http://www.opensource.org/licenses/mit-license.php | |||
*/ | |||
class Bbcode { | |||
protected $tagTypes = []; | |||
/** | |||
* The text with BBCodes | |||
* | |||
* @var string|null | |||
*/ | |||
protected $text = null; | |||
/** | |||
* BBCode constructor. | |||
* | |||
* @param string|null $text The text - might include BBCode tags | |||
*/ | |||
public function __construct($text = null) | |||
{ | |||
$this->setText($text); | |||
} | |||
/** | |||
* Set the raw text - might include BBCode tags | |||
* | |||
* @param string $text The text | |||
* @retun void | |||
*/ | |||
public function setText($text) | |||
{ | |||
$this->text = $text; | |||
} | |||
/** | |||
* Renders only the text without any BBCode tags. | |||
* | |||
* @param string $text Optional: Render the passed BBCode string instead of the internally stored one | |||
* @return string | |||
*/ | |||
public function renderPlain($text = null) | |||
{ | |||
if ($this->text !== null and $text === null) { | |||
$text = $this->text; | |||
} | |||
return preg_replace("/\[(.*?)\]/is", '', $text); | |||
} | |||
/** | |||
* Renders only the text without any BBCode tags. | |||
* Alias for renderRaw(). | |||
* | |||
* @deprecated Deprecated since 1.1.0 | |||
* | |||
* @param string $text Optional: Render the passed BBCode string instead of the internally stored one | |||
* @return string | |||
*/ | |||
public function renderRaw($text = null) | |||
{ | |||
return $this->renderPlain($text); | |||
} | |||
/** | |||
* Renders BBCode to HTML | |||
* | |||
* @param string $text Optional: Render the passed BBCode string instead of the internally stored one | |||
* @param bool $escape Escape HTML entities? (Only "<" and ">"!) | |||
* @param bool $keepLines Keep line breaks by replacing them with <br>? | |||
* @return string | |||
*/ | |||
public function render($text = null, $escape = true, $keepLines = true) | |||
{ | |||
if ($this->text !== null and $text === null) { | |||
$text = $this->text; | |||
} | |||
$tokenizer = new Tokenizer($this->tagTypes); | |||
$tokens = $tokenizer->tokenize($text, $escape, $keepLines); | |||
$html = ''; | |||
$level = 0; | |||
foreach ($tokens as $index => $token) { | |||
switch ($token->getType()) { | |||
case Token::TYPE_LINEBREAK: | |||
$html .= '<br/>'; | |||
break; | |||
case Token::TYPE_PLAIN_TEXT: | |||
$html .= $token->getValue(); | |||
break; | |||
case Token::TYPE_TAG_OPENING: | |||
case Token::TYPE_TAG_CLOSING: | |||
$tagOpening = $token->getType() === Token::TYPE_TAG_OPENING; | |||
$tagName = $token->getValue(); | |||
// If the tag is not known, just do not render it. We do not want to throw any exceptions. | |||
if (isset($this->tagTypes[$tagName])) { | |||
$tagType = $this->tagTypes[$tagName]; | |||
/** @var AbstractTagType $tag */ | |||
$tag = new $tagType; | |||
$tag->render($html, $tagOpening); | |||
} | |||
if ($tagOpening) { | |||
$level++; | |||
} else { | |||
$level--; | |||
} | |||
} | |||
} | |||
return $html; | |||
$html = ''; | |||
$len = mb_strlen($text); | |||
$inTag = false; // True if current position is inside a tag | |||
$inName = false; // True if current pos is inside a tag name | |||
$inStr = false; // True if current pos is inside a string | |||
/** @var Tag|null $tag */ | |||
$tag = null; | |||
$openTags = array(); | |||
/* | |||
* Loop over each character of the text | |||
*/ | |||
for ($i = 0; $i < $len; $i++) { | |||
$char = mb_substr($text, $i, 1); | |||
if ($keepLines) { | |||
if ($char == "\n") { | |||
$html .= '<br/>'; | |||
} | |||
if ($char == "\r") { | |||
continue; | |||
} | |||
} | |||
if (! $escape or ($char != '<' and $char != '>')) { | |||
/* | |||
* $inTag == true means the current position is inside a tag definition | |||
* (= inside the brackets of a tag) | |||
*/ | |||
if ($inTag) { | |||
if ($char == '"') { | |||
if ($inStr) { | |||
$inStr = false; | |||
} else { | |||
if ($inName) { | |||
$tag->valid = false; | |||
} else { | |||
$inStr = true; | |||
} | |||
} | |||
} else { | |||
/* | |||
* This closes a tag | |||
*/ | |||
if ($char == ']' and ! $inStr) { | |||
$inTag = false; | |||
$inName = false; | |||
if ($tag->valid) { | |||
$code = null; | |||
if ($tag->opening) { | |||
$code = $this->generateTag($tag, $html, null, $openTags); | |||
} else { | |||
$openingTag = $this->popTag($openTags, $tag); | |||
if ($openingTag) { | |||
$code = $this->generateTag($tag, $html, $openingTag, $openTags); | |||
} | |||
} | |||
if ($code !== null and $tag->opening) { | |||
$openTags[$tag->name][] = $tag; | |||
} | |||
$html .= $code; | |||
} | |||
continue; | |||
} | |||
if ($inName and ! $inStr) { | |||
/* | |||
* This makes the current tag a closing tag | |||
*/ | |||
if ($char == '/') { | |||
if ($tag->name) { | |||
$tag->valid = false; | |||
} else { | |||
$tag->opening = false; | |||
} | |||
} else { | |||
/* | |||
* This means a property starts | |||
*/ | |||
if ($char == '=') { | |||
if ($tag->name) { | |||
$inName = false; | |||
} else { | |||
$tag->valid = false; | |||
} | |||
} else { | |||
$tag->name .= mb_strtolower($char); | |||
} | |||
} | |||
} else { // If we are not inside the name we are inside a property | |||
$tag->property .= $char; | |||
} | |||
} | |||
} else { | |||
/* | |||
* This opens a tag | |||
*/ | |||
if ($char == '[') { | |||
$inTag = true; | |||
$inName = true; | |||
$tag = new Tag(); | |||
$tag->position = mb_strlen($html); | |||
} else { | |||
$html .= $char; | |||
} | |||
} | |||
} else { | |||
$html .= htmlspecialchars($char); | |||
} | |||
} | |||
/* | |||
* Check for tags that are not closed and close them. | |||
*/ | |||
foreach ($openTags as $name => $openTagsByType) { | |||
$closingTag = new Tag($name, false); | |||
foreach ($openTagsByType as $openTag) { | |||
$html .= $this->generateTag($closingTag, $html, $openTag); | |||
} | |||
} | |||
return $html; | |||
} | |||
/** | |||
* Generates and returns the HTML code of the current tag | |||
* | |||
* @param Tag $tag The current tag | |||
* @param string $html The current HTML code passed by reference - might be altered! | |||
* @param Tag|null $openingTag The opening tag that is linked to the tag (or null) | |||
* @param Tag[] $openTags Array with tags that are opned but not closed | |||
* @return string | |||
*/ | |||
protected function generateTag(Tag $tag, &$html, Tag $openingTag = null, array $openTags = []) | |||
{ | |||
$code = null; | |||
if (in_array($tag->name, $this->ignoredTags)) { | |||
return $code; | |||
} | |||
// Custom tags: | |||
foreach ($this->customTagClosures as $name => $closure) { | |||
if ($tag->name === $name) { | |||
exit($tag->name); | |||
$code .= $closure($tag, $html, $openingTag); | |||
} | |||
} | |||
return $code; | |||
} | |||
/** | |||
* Magic method __toString() | |||
* | |||
* @return string | |||
*/ | |||
public function __toString() | |||
{ | |||
return $this->render(); | |||
} | |||
/** | |||
* Returns the last tag of a given type and removes it from the array. | |||
* | |||
* @param Tag[] $tags Array of tags | |||
* @param Tag $tag Return the last tag of the type of this tag | |||
* @return Tag|null | |||
*/ | |||
protected function popTag(array &$tags, $tag) | |||
{ | |||
if (! isset($tags[$tag->name])) { | |||
return null; | |||
} | |||
$size = sizeof($tags[$tag->name]); | |||
if ($size === 0) { | |||
return null; | |||
} else { | |||
return array_pop($tags[$tag->name]); | |||
} | |||
} | |||
/** | |||
* Returns true if $haystack ends with $needle | |||
* | |||
* @param string $haystack | |||
* @param string $needle | |||
* @return bool | |||
*/ | |||
protected function endsWith($haystack, $needle) | |||
{ | |||
return ($needle === '' or mb_substr($haystack, -mb_strlen($needle)) === $needle); | |||
} | |||
} |
@@ -1,21 +0,0 @@ | |||
MIT License | |||
Copyright (c) 2017 Chris Konnertz | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -1,67 +0,0 @@ | |||
<?php | |||
namespace App\Vendor\BBCode; | |||
/** | |||
* This class represents a single BBCode tag. | |||
* It is just a simple class used for storing tag. | |||
* | |||
* @package ChrisKonnertz\BBCode | |||
*/ | |||
class Tag { | |||
/** | |||
* The name of the tag | |||
* | |||
* @var string|null | |||
*/ | |||
public $name = null; | |||
/** | |||
* The value of the property | |||
* | |||
* @var string | |||
*/ | |||
public $property = null; | |||
/** | |||
* Is this an opening tag (true)? | |||
* | |||
* @var bool | |||
*/ | |||
public $opening = true; | |||
/** | |||
* Is this tag valid? | |||
* | |||
* @var bool | |||
*/ | |||
public $valid = true; | |||
/** | |||
* Position of this tag inside the whole BBCode string | |||
* | |||
* @var int | |||
*/ | |||
public $position = -1; | |||
/** | |||
* Tag constructor. | |||
* | |||
* @param string|null $name The name of the tag | |||
* @param bool $opening Is this an opening tag (true)? | |||
*/ | |||
public function __construct($name = null, $opening = true) | |||
{ | |||
if ($name !== null and ! is_string($name)) { | |||
throw new \InvalidArgumentException('The "name" parameter has to be of type string'); | |||
} | |||
if (! is_bool($opening)) { | |||
throw new \InvalidArgumentException('The "opening" parameter has to be of type bool'); | |||
} | |||
$this->name = $name; | |||
$this->opening = $opening; | |||
} | |||
} |
@@ -1,32 +0,0 @@ | |||
<?php | |||
namespace App\Vendor\BBCode\Tags; | |||
/** | |||
* This class is the abstract base class for all BBCode tag classes. | |||
*/ | |||
abstract class AbstractTag | |||
{ | |||
/** | |||
* The name of the tag type (lower cased). | |||
* The sub class has to overwrite this constant. | |||
*/ | |||
const NAME = ''; | |||
/** | |||
* If true, inner tags will be treated as plain text | |||
* The sub class has to overwrite this constant. | |||
*/ | |||
const NO_PARSE = false; | |||
/** | |||
* This method renders a tag of this type. | |||
* Has to return something, at least an empty string. | |||
* | |||
* @param string $html The generated HTML code so far - passed by reference | |||
* @param bool $opening Is the tag opening (true) or closing (false)? | |||
* @return void | |||
*/ | |||
abstract public function render(&$html, $opening); | |||
} |
@@ -1,169 +0,0 @@ | |||
<?php | |||
namespace App\Vendor\BBCode\Tokenizer; | |||
/** | |||
* The tokenizer splits a term into an array of tokens. | |||
* Tokens are the parts of a text. | |||
*/ | |||
class Token | |||
{ | |||
/** | |||
* Defines the type of a token. | |||
* Example token value of a token with this type: | |||
* '\n' | |||
* | |||
* @const int | |||
*/ | |||
const TYPE_LINEBREAK = 0; | |||
/** | |||
* Defines the type of a token. | |||
* Example token value of a token with this type: | |||
* 'Hello world' | |||
* | |||
* @const int | |||
*/ | |||
const TYPE_PLAIN_TEXT = 1; | |||
/** | |||
* Defines the type of a token. | |||
* Example token value of a token with this type: | |||
* '[b]' | |||
* | |||
* @const int | |||
*/ | |||
const TYPE_TAG_OPENING = 2; | |||
/** | |||
* Defines the type of a token. | |||
* Example token value of a token with this type: | |||
* '[/b]' | |||
* | |||
* @const int | |||
*/ | |||
const TYPE_TAG_CLOSING = 3; | |||
/** | |||
* The raw value of the token. Numbers are stored as string. | |||
* | |||
* @var string | |||
*/ | |||
protected $value = null; | |||
/** | |||
* The type of the token. One of these constants: | |||
* self::TYPE_WORD|self::TYPE_NUMBER|self::TYPE_CHARACTER | |||
* | |||
* @var int | |||
*/ | |||
protected $type; | |||
/** | |||
* Position of the token in the input stream. | |||
* It is stored as a debugging information. | |||
* | |||
* @var int | |||
*/ | |||
protected $position; | |||
/** | |||
* The property value of the token (empty string = none) | |||
* | |||
* @var string | |||
*/ | |||
protected $property; | |||
/** | |||
* Token constructor. The position must be >= 0. | |||
* | |||
* @param string $value The value of the token | |||
* @param string $type The type of the token - one of these: self::TYPE_<NAME> | |||
* @param int $position The position of the token in the original text | |||
* @param string $property Optional: The property value of the token | |||
*/ | |||
public function __construct($value, $type, $position, $property = '') | |||
{ | |||
if (! is_string($value)) { | |||
throw new \InvalidArgumentException( | |||
'Error: Argument "value" has to be of type string but is of type "'.gettype($value).'"' | |||
); | |||
} | |||
$this->value = $value; | |||
if (! in_array($type, $this->getAllTypes())) { | |||
throw new \InvalidArgumentException( | |||
'Error: Argument "type" does not have the value of a known token type' | |||
); | |||
} | |||
$this->type = $type; | |||
if (! is_int($position)) { | |||
throw new \InvalidArgumentException('Error: Argument "position" has to be of type int'); | |||
} | |||
if ($position < 0) { | |||
throw new \InvalidArgumentException('Error: Value of parameter "position" has to be >= zero'); | |||
} | |||
$this->position = $position; | |||
if (! is_string($property)) { | |||
throw new \InvalidArgumentException( | |||
'Error: Argument "property" has to be of type string but is of type "'.gettype($property).'"' | |||
); | |||
} | |||
$this->property = $property; | |||
} | |||
/** | |||
* Returns an array that contains the values of all | |||
* possible types of token type constants. | |||
* | |||
* @see self::TYPE_<NAME> | |||
* | |||
* @return int[] | |||
*/ | |||
public function getAllTypes() | |||
{ | |||
return [self::TYPE_LINEBREAK, self::TYPE_PLAIN_TEXT, self::TYPE_TAG_OPENING, self::TYPE_TAG_CLOSING]; | |||
} | |||
/** | |||
* Getter for the value | |||
* | |||
* @return string | |||
*/ | |||
public function getValue() | |||
{ | |||
return $this->value; | |||
} | |||
/** | |||
* Getter for the type | |||
* | |||
* @return string | |||
*/ | |||
public function getType() | |||
{ | |||
return $this->type; | |||
} | |||
/** | |||
* Getter for the position | |||
* | |||
* @return int | |||
*/ | |||
public function getPosition() | |||
{ | |||
return $this->position; | |||
} | |||
/** | |||
* @return string | |||
*/ | |||
public function __toString() | |||
{ | |||
return $this->value; | |||
} | |||
} |
@@ -1,149 +0,0 @@ | |||
<?php | |||
namespace App\Vendor\BBCode\Tokenizer; | |||
use App\Vendor\BBCode\BBCode; | |||
use App\Vendor\BBCode\Tag\AbstractTagType; | |||
/** | |||
* "Tokenization is the process of demarcating and possibly classifying | |||
* sections of a string of input characters" (Source: Wikipedia) | |||
* The tokenizer operates on the text and tries to split it into parts. | |||
* The tokenizer is not very smart, it does not really care for grammar. | |||
*/ | |||
class Tokenizer | |||
{ | |||
/** | |||
* Tokenize the text. Returns an array with the tokens. | |||
* Note: There can be more than one subsequent plain text tokens | |||
* | |||
* @param string $text Render the passed BBCode string | |||
* @param bool $escape Escape HTML entities? (Only "<" and ">"!) | |||
* @param bool $keepLines Keep line breaks by replacing them with <br>? | |||
* @return Token[] | |||
*/ | |||
public function tokenize($text, $escape = true, $keepLines = true) | |||
{ | |||
$tokens = []; | |||
$length = mb_strlen($text); | |||
$value = ''; | |||
$insideTag = false; // Means: The current position is between "[" and "]" (=a tag definition) | |||
$insideName = false; // In a tag "[code]", "code" is the name of the tag | |||
$insideString = false; // Properties of tags can be written as string with " at the start & end | |||
$noParse = false; // If true, do not parse BBCode inside this tag | |||
$tagName = ''; // Name of the current tag | |||
$tagProperty = ''; // Property value of the current tag | |||
$tagOpening = null; // True/false + null = undefined | |||
$tagStartPos = 0; | |||
// Loop over each character of the text | |||
for ($pos = 0; $pos < $length; $pos++) { | |||
$char = mb_substr($text, $pos, 1); | |||
if ($keepLines) { | |||
// Create line break token when \n | |||
if ($char === "\n") { | |||
$tokens[] = new Token($char, Token::TYPE_LINEBREAK, $pos); | |||
} | |||
// Ignore \r | |||
if ($char === "\r") { | |||
continue; | |||
} | |||
} | |||
if (! $escape or ($char !== '<' and $char !== '>')) { | |||
if ($insideTag) { | |||
if ($char === '"') { | |||
if ($insideString) { | |||
$insideString = false; | |||
} else { | |||
$insideString = true; | |||
} | |||
} else { | |||
// "]" closes a tag (if it is not used in a string) | |||
if ($char == ']' and ! $insideString) { | |||
if (! $noParse or (! $tagOpening and $this->checkNoParse($value))) { | |||
$tokenType = $tagOpening ? Token::TYPE_TAG_OPENING : Token::TYPE_TAG_CLOSING; | |||
$tokens[] = new Token($tagName, $tokenType, $tagStartPos, $tagProperty); | |||
} else { | |||
$tokens[] = new Token($value, Token::TYPE_PLAIN_TEXT, $tagStartPos); | |||
} | |||
$noParse = $this->checkNoParse($value); | |||
$tagName = ''; | |||
$value = ''; | |||
$insideTag = false; | |||
$insideName = false; | |||
continue; | |||
} | |||
if ($insideName and ! $insideString) { | |||
// This makes the current tag a closing tag | |||
if ($char === '/') { | |||
$tagOpening = false; | |||
} else { | |||
// This means a property starts | |||
if ($char === '=') { | |||
$insideName = false; | |||
} elseif ($char === '[') { // Invalid tag - ignore it and start again | |||
$value = ''; | |||
$tagName = ''; | |||
$tagOpening = true; | |||
} | |||
else { | |||
$value .= mb_strtolower($char); | |||
$tagName .= mb_strtolower($char); | |||
} | |||
} | |||
} else { // If we are not inside the name we are inside a property | |||
$tagProperty .= $char; | |||
} | |||
} | |||
} else { | |||
if ($char === '[') { | |||
// Since a tag starts, plain text may end and we have to create a token for it | |||
if ($value !== '') { | |||
$tokens[] = new Token($value, Token::TYPE_PLAIN_TEXT, $tagStartPos); | |||
$value = ''; | |||
} | |||
$insideTag = true; | |||
$insideName = true; | |||
$tagOpening = true; | |||
$tagStartPos = $pos; | |||
$tagName = ''; | |||
} else { | |||
// This is plain text | |||
$value .= $char; | |||
} | |||
} | |||
} else { | |||
// Escape HTML chars "<" and ">" | |||
$value .= htmlspecialchars($char); | |||
} | |||
} | |||
// If the text ends with plain text we have to create the final plain text token now | |||
if ($value !== '') { | |||
$tokens[] = new Token($value, Token::TYPE_PLAIN_TEXT, $tagStartPos); | |||
} | |||
return $tokens; | |||
} | |||
/** | |||
* Check if a tag is a tag that forbids parsing of its inner content | |||
* | |||
* @param string $tagName | |||
* @return bool | |||
*/ | |||
protected function checkNoParse($tagName) | |||
{ | |||
// We do not want to throw any exceptions so we just return false | |||
return false; | |||
} | |||
} |
@@ -4,10 +4,17 @@ | |||
"keywords": ["forum", "web1.0", "client"], | |||
"license": "AGPL3", | |||
"type": "project", | |||
"repositories": [ | |||
{ | |||
"type": "vcs", | |||
"url": "https://git.mos6581.com/Knockout/BBCode.git" | |||
} | |||
], | |||
"require": { | |||
"php": "^7.4", | |||
"laravel/lumen-framework": "^8.0", | |||
"nesbot/carbon": "^2.43" | |||
"nesbot/carbon": "^2.43", | |||
"knockout/bbcode": "dev-master" | |||
}, | |||
"require-dev": { | |||
"barryvdh/laravel-debugbar": "^3.5", | |||
@@ -4,7 +4,7 @@ | |||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | |||
"This file is @generated automatically" | |||
], | |||
"content-hash": "0ce9dd22cd325df894b40d30590f0cdb", | |||
"content-hash": "383a2d2ab44182ee3aa0814ff7b823b9", | |||
"packages": [ | |||
{ | |||
"name": "brick/math", | |||
@@ -62,55 +62,6 @@ | |||
], | |||
"time": "2020-08-18T23:57:15+00:00" | |||
}, | |||
{ | |||
"name": "chriskonnertz/bbcode", | |||
"version": "v1.1.2", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/chriskonnertz/bbcode.git", | |||
"reference": "d3acd447ee11265d4ef38b9058cef32adcafa245" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/chriskonnertz/bbcode/zipball/d3acd447ee11265d4ef38b9058cef32adcafa245", | |||
"reference": "d3acd447ee11265d4ef38b9058cef32adcafa245", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3.7" | |||
}, | |||
"require-dev": { | |||
"phpunit/phpunit": "~4" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"ChrisKonnertz\\BBCode": "src/" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Kai Mallea", | |||
"email": "kmallea@gmail.com" | |||
}, | |||
{ | |||
"name": "Chris Konnertz" | |||
} | |||
], | |||
"description": "A naive attempt at a BBCode 'parser' written in PHP. It uses regex and thus fails at complex, nested tags.", | |||
"keywords": [ | |||
"bbcode" | |||
], | |||
"support": { | |||
"issues": "https://github.com/chriskonnertz/bbcode/issues", | |||
"source": "https://github.com/chriskonnertz/bbcode/tree/master" | |||
}, | |||
"time": "2018-06-17T13:58:51+00:00" | |||
}, | |||
{ | |||
"name": "doctrine/inflector", | |||
"version": "2.0.3", | |||
@@ -1910,6 +1861,33 @@ | |||
}, | |||
"time": "2020-11-02T14:01:41+00:00" | |||
}, | |||
{ | |||
"name": "knockout/bbcode", | |||
"version": "dev-master", | |||
"source": { | |||
"type": "git", | |||
"url": "https://git.mos6581.com/Knockout/BBCode.git", | |||
"reference": "c06bf463f5c701c398852b413c1a8210c5c9c165" | |||
}, | |||
"require": { | |||
"php": ">=7.0" | |||
}, | |||
"require-dev": { | |||
"phpunit/phpunit": "5.6.*" | |||
}, | |||
"default-branch": true, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"Knockout": "src/" | |||
} | |||
}, | |||
"license": [ | |||
"MIT" | |||
], | |||
"description": "php parser for bb code", | |||
"time": "2021-01-01T23:15:08+00:00" | |||
}, | |||
{ | |||
"name": "laravel/lumen-framework", | |||
"version": "v8.2.1", | |||
@@ -7391,7 +7369,9 @@ | |||
], | |||
"aliases": [], | |||
"minimum-stability": "dev", | |||
"stability-flags": [], | |||
"stability-flags": { | |||
"knockout/bbcode": 20 | |||
}, | |||
"prefer-stable": true, | |||
"prefer-lowest": false, | |||
"platform": { | |||
@@ -0,0 +1,6 @@ | |||
<div class="quote"> | |||
<a class="user" href="{{ route('thread', ['thread' => $thread, 'page' => $page]) }}#post-{{ $postId }}">{{ $username }}</a> | |||
<div class="postContent"> | |||
{!! $content !!} | |||
</div> | |||
</div> |
@@ -6,7 +6,7 @@ | |||
<div class="userForeground"> | |||
<img class="avatar" src="{{ $post->user->avatar }}" height="32px" /> | |||
<a href="/user/{{ $post->user->id }}" class="username">{{ $post->user->username }}</a> | |||
<a class="date" href="#post-{{ $post->id }}">9 months ago</a> | |||
<a class="date" href="#post-{{ $post->id }}">{{ $post->dateDiff() }}</a> | |||
</div> | |||
</div> | |||
<div class="row post"> | |||