Upgrade to 4.0.0
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user