Upgrade to 4.0.0

This commit is contained in:
Bastian Allgeier
2023-11-28 09:33:56 +01:00
parent f96b96af76
commit 3b0b6546ca
480 changed files with 21371 additions and 13327 deletions

View File

@@ -21,22 +21,21 @@ use Kirby\Toolkit\Str;
*/
class Element
{
protected array $marks;
protected DOMElement $node;
public function __construct(DOMElement $node, array $marks = [])
{
$this->marks = $marks;
$this->node = $node;
public function __construct(
protected DOMElement $node,
protected array $marks = []
) {
}
/**
* The returns the attribute value or
* the given fallback if the attribute does not exist
*/
public function attr(string $attr, string|null $fallback = null): string|null
{
if ($this->node->hasAttribute($attr)) {
public function attr(
string $attr,
string|null $fallback = null
): string|null {
if ($this->node->hasAttribute($attr) === true) {
return $this->node->getAttribute($attr) ?? $fallback;
}
@@ -112,7 +111,9 @@ class Element
*/
public function innerHtml(array|null $marks = null): string
{
return (new Inline($this->node, $marks ?? $this->marks))->innerHtml();
$marks ??= $this->marks;
$inline = new Inline($this->node, $marks);
return $inline->innerHtml();
}
/**

View File

@@ -2,7 +2,7 @@
namespace Kirby\Parsley;
use DOMComment;
use DOMElement;
use DOMNode;
use DOMNodeList;
use DOMText;
@@ -52,21 +52,22 @@ class Inline
}
/**
* Get all allowed attributes for a DOMNode
* Get all allowed attributes for a DOMElement
* as clean array
*/
public static function parseAttrs(DOMNode $node, array $marks = []): array
{
public static function parseAttrs(
DOMElement $node,
array $marks = []
): array {
$attrs = [];
$mark = $marks[$node->tagName];
$defaults = $mark['defaults'] ?? [];
foreach ($mark['attrs'] ?? [] as $attr) {
if ($node->hasAttribute($attr)) {
$attrs[$attr] = $node->getAttribute($attr);
} else {
$attrs[$attr] = $defaults[$attr] ?? null;
}
$attrs[$attr] = match ($node->hasAttribute($attr)) {
true => $node->getAttribute($attr),
default => $defaults[$attr] ?? null
};
}
return $attrs;
@@ -76,8 +77,10 @@ class Inline
* Parses all children and creates clean HTML
* for each of them.
*/
public static function parseChildren(DOMNodeList $children, array $marks): string
{
public static function parseChildren(
DOMNodeList $children,
array $marks
): string {
$html = '';
foreach ($children as $child) {
$html .= static::parseNode($child, $marks);
@@ -89,8 +92,10 @@ class Inline
* Go through all child elements and create
* clean inner HTML for them
*/
public static function parseInnerHtml(DOMNode $node, array $marks = []): string|null
{
public static function parseInnerHtml(
DOMElement $node,
array $marks = []
): string|null {
$html = static::parseChildren($node->childNodes, $marks);
// trim the inner HTML for paragraphs
@@ -115,33 +120,35 @@ class Inline
return Html::encode($node->textContent);
}
// ignore comments
if ($node instanceof DOMComment) {
return null;
if ($node instanceof DOMElement) {
// unknown marks
if (array_key_exists($node->tagName, $marks) === false) {
return static::parseChildren($node->childNodes, $marks);
}
// collect all allowed attributes
$attrs = static::parseAttrs($node, $marks);
// close self-closing elements
if (Html::isVoid($node->tagName) === true) {
return '<' . $node->tagName . Html::attr($attrs, null, ' ') . ' />';
}
$innerHtml = static::parseInnerHtml($node, $marks);
// skip empty paragraphs
if ($innerHtml === null && $node->tagName === 'p') {
return null;
}
// create the outer html for the element
$html = '<' . $node->tagName . Html::attr($attrs, null, ' ') . '>';
$html .= $innerHtml;
$html .= '</' . $node->tagName . '>';
return $html;
}
// unknown marks
if (array_key_exists($node->tagName, $marks) === false) {
return static::parseChildren($node->childNodes, $marks);
}
// collect all allowed attributes
$attrs = static::parseAttrs($node, $marks);
// close self-closing elements
if (Html::isVoid($node->tagName) === true) {
return '<' . $node->tagName . Html::attr($attrs, null, ' ') . ' />';
}
$innerHtml = static::parseInnerHtml($node, $marks);
// skip empty paragraphs
if ($innerHtml === null && $node->tagName === 'p') {
return null;
}
// create the outer html for the element
return '<' . $node->tagName . Html::attr($attrs, null, ' ') . '>' . $innerHtml . '</' . $node->tagName . '>';
return null;
}
/**

View File

@@ -40,10 +40,8 @@ class Parsley
// or should be skipped
if ($this->useXmlExtension() === false) {
$this->blocks[] = [
'type' => 'markdown',
'content' => [
'text' => $html,
]
'type' => 'markdown',
'content' => ['text' => $html]
];
return;
}
@@ -104,7 +102,10 @@ class Parsley
}
foreach ($element->childNodes as $childNode) {
if ($this->isBlock($childNode) === true || $this->containsBlock($childNode)) {
if (
$this->isBlock($childNode) === true ||
$this->containsBlock($childNode)
) {
return true;
}
}
@@ -129,7 +130,7 @@ class Parsley
$html = [];
foreach ($this->inline as $inline) {
$node = new Inline($inline, $this->marks);
$node = new Inline($inline, $this->marks);
$html[] = $node->innerHTML();
}
@@ -161,11 +162,11 @@ class Parsley
*/
public function isBlock(DOMNode $element): bool
{
if ($element instanceof DOMElement === false) {
return false;
if ($element instanceof DOMElement) {
return array_key_exists($element->tagName, $this->nodes) === true;
}
return array_key_exists($element->tagName, $this->nodes) === true;
return false;
}
/**
@@ -204,7 +205,11 @@ class Parsley
$lastItem = $this->blocks[$lastIndex] ?? null;
// merge with previous block
if ($block['type'] === 'text' && $lastItem && $lastItem['type'] === 'text') {
if (
$block['type'] === 'text' &&
$lastItem &&
$lastItem['type'] === 'text'
) {
$this->blocks[$lastIndex]['content']['text'] .= ' ' . $block['content']['text'];
// append
@@ -227,15 +232,18 @@ class Parsley
}
// inline context
if ($this->isInline($element)) {
if ($this->isInline($element) === true) {
$this->inline[] = $element;
return true;
} else {
$this->endInlineBlock();
}
$this->endInlineBlock();
// known block nodes
if ($this->isBlock($element) === true) {
/**
* @var DOMElement $element
*/
if ($parser = ($this->nodes[$element->tagName]['parse'] ?? null)) {
if ($result = $parser(new Element($element, $this->marks))) {
$this->blocks[] = $result;
@@ -246,6 +254,9 @@ class Parsley
// has only unknown children (div, etc.)
if ($this->containsBlock($element) === false) {
/**
* @var DOMElement $element
*/
if (in_array($element->tagName, $this->skip) === true) {
return false;
}

View File

@@ -23,8 +23,7 @@ class Blocks extends Plain
{
public function blockquote(Element $node): array
{
$citation = null;
$text = [];
$text = [];
// get all the text for the quote
foreach ($node->children() as $child) {
@@ -36,7 +35,8 @@ class Blocks extends Plain
$child instanceof DOMElement &&
$child->tagName !== 'footer'
) {
$text[] = (new Element($child))->innerHTML($this->marks());
$element = new Element($child);
$text[] = $element->innerHTML($this->marks());
}
}
@@ -44,9 +44,7 @@ class Blocks extends Plain
$text = implode('', array_filter($text));
// get the citation from the footer
if ($footer = $node->find('footer')) {
$citation = $footer->innerHTML($this->marks());
}
$citation = $node->find('footer')?->innerHTML($this->marks());
return [
'content' => [
@@ -115,15 +113,12 @@ class Blocks extends Plain
public function iframe(Element $node): array
{
$caption = null;
$src = $node->attr('src');
$src = $node->attr('src');
$figcaption = $node->find('ancestor::figure[1]//figcaption');
$caption = $figcaption?->innerHTML($this->marks());
if ($figcaption = $node->find('ancestor::figure[1]//figcaption')) {
$caption = $figcaption->innerHTML($this->marks());
// avoid parsing the caption twice
$figcaption->remove();
}
// avoid parsing the caption twice
$figcaption?->remove();
// reverse engineer video URLs
if (preg_match('!player.vimeo.com\/video\/([0-9]+)!i', $src, $array) === 1) {
@@ -157,19 +152,12 @@ class Blocks extends Plain
public function img(Element $node): array
{
$caption = null;
$link = null;
$link = $node->find('ancestor::a')?->attr('href');
$figcaption = $node->find('ancestor::figure[1]//figcaption');
$caption = $figcaption?->innerHTML($this->marks());
if ($figcaption = $node->find('ancestor::figure[1]//figcaption')) {
$caption = $figcaption->innerHTML($this->marks());
// avoid parsing the caption twice
$figcaption->remove();
}
if ($a = $node->find('ancestor::a')) {
$link = $a->attr('href');
}
// avoid parsing the caption twice
$figcaption?->remove();
return [
'content' => [
@@ -198,19 +186,21 @@ class Blocks extends Plain
$innerHtml .= $child->textContent;
} elseif ($child instanceof DOMElement) {
$child = new Element($child);
if (in_array($child->tagName(), ['ul', 'ol']) === true) {
$innerHtml .= $this->list($child);
} else {
$innerHtml .= $child->innerHTML($this->marks());
}
$list = ['ul', 'ol'];
$innerHtml .= match (in_array($child->tagName(), $list)) {
true => $this->list($child),
default => $child->innerHTML($this->marks())
};
}
}
$html[] = '<li>' . trim($innerHtml) . '</li>';
}
return '<' . $node->tagName() . '>' . implode($html) . '</' . $node->tagName() . '>';
$outerHtml = '<' . $node->tagName() . '>';
$outerHtml .= implode($html);
$outerHtml .= '</' . $node->tagName() . '>';
return $outerHtml;
}
/**