]> git.mxchange.org Git - friendica.git/blobdiff - src/Content/Text/NPF.php
Unified BBCode conversion, improved proxy functionality
[friendica.git] / src / Content / Text / NPF.php
index 608cfc2e37016561a7b257130346b49fd5c19044..f5a6e2dc188baf09a24f95accfdb71895a918b1a 100644 (file)
@@ -23,7 +23,6 @@ namespace Friendica\Content\Text;
 
 use DOMDocument;
 use DOMElement;
-use DOMNode;
 use Friendica\Model\Photo;
 use Friendica\Model\Post;
 
@@ -33,166 +32,480 @@ use Friendica\Model\Post;
  */
 class NPF
 {
-       static public function fromBBCode(string $bbcode, int $uri_id): array
+       private static $heading_subtype = [];
+
+       /**
+        * Convert BBCode into NPF (Tumblr Neue Post Format)
+        *
+        * @param string $bbcode
+        * @param integer $uri_id
+        * @return array NPF
+        */
+       public static function fromBBCode(string $bbcode, int $uri_id): array
        {
-               $npf = [];
-
                $bbcode = self::prepareBody($bbcode);
 
-               $html = BBCode::convert($bbcode, false, BBCode::CONNECTORS);
+               $html = BBCode::convertForUriId($uri_id, $bbcode, BBCode::NPF);
                if (empty($html)) {
                        return [];
                }
 
                $doc = new DOMDocument();
+
+               $doc->formatOutput = true;
                if (!@$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'))) {
                        return [];
                }
 
-               $node = $doc->getElementsByTagName('body')->item(0);
-               foreach ($node->childNodes as $child) {
-                       $npf = self::routeElements($child, $uri_id, $npf);
-               }
+               self::setHeadingSubStyles($doc);
+
+               $element = $doc->getElementsByTagName('body')->item(0);
+
+               list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, true, []);
 
-               return self::addLinkBlock($uri_id, $npf);
+               return self::addLinkBlockForUriId($uri_id, 0, $npf);
        }
 
-       public static function prepareBody(string $body): string
+       /**
+        * Fetch the heading types
+        *
+        * @param DOMDocument $doc
+        * @return void
+        */
+       private static function setHeadingSubStyles(DOMDocument $doc)
        {
-               $shared = BBCode::fetchShareAttributes($body);
-               if (!empty($shared)) {
-                       $body = $shared['shared'];
+               self::$heading_subtype = [];
+               foreach (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as $element) {
+                       if ($doc->getElementsByTagName($element)->count() > 0) {
+                               if (empty(self::$heading_subtype)) {
+                                       self::$heading_subtype[$element] = 'heading1';
+                               } else {
+                                       self::$heading_subtype[$element] = 'heading2';
+                               }
+                       }
                }
+       }
 
-               $body = BBCode::removeAttachment($body);
+       /**
+        * Prepare the BBCode for the NPF conversion
+        *
+        * @param string $bbcode
+        * @return string
+        */
+       private static function prepareBody(string $bbcode): string
+       {
+               $shared = BBCode::fetchShareAttributes($bbcode);
+               if (!empty($shared)) {
+                       $bbcode = $shared['shared'];
+               }
 
-               $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
+               $bbcode = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $bbcode);
 
-               if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
+               if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $bbcode, $pictures, PREG_SET_ORDER)) {
                        foreach ($pictures as $picture) {
                                if (preg_match('#/photo/.*-[01]\.#ism', $picture[2]) && (preg_match('#/photo/.*-0\.#ism', $picture[1]) || preg_match('#/photos/.*/image/#ism', $picture[1]))) {
-                                       $body = str_replace($picture[0], "\n\n[img=" . str_replace('-1.', '-0.', $picture[2]) . "]" . $picture[3] . "[/img]\n\n", $body);
+                                       $bbcode = str_replace($picture[0], "\n\n[img=" . str_replace('-1.', '-0.', $picture[2]) . "]" . $picture[3] . "[/img]\n\n", $bbcode);
                                }
                        }
                }
 
-               $body = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", "\n\n[img=$1]$2[/img]\n\n", $body);
+               $bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", "\n\n[img=$1]$2[/img]\n\n", $bbcode);
 
-               if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
+               if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $bbcode, $pictures, PREG_SET_ORDER)) {
                        foreach ($pictures as $picture) {
                                if (preg_match('#/photo/.*-[01]\.#ism', $picture[2]) && (preg_match('#/photo/.*-0\.#ism', $picture[1]) || preg_match('#/photos/.*/image/#ism', $picture[1]))) {
-                                       $body = str_replace($picture[0], "\n\n[img]" . str_replace('-1.', '-0.', $picture[2]) . "[/img]\n\n", $body);
+                                       $bbcode = str_replace($picture[0], "\n\n[img]" . str_replace('-1.', '-0.', $picture[2]) . "[/img]\n\n", $bbcode);
                                }
                        }
                }
 
-               $body = preg_replace("/\[img\](.*?)\[\/img\]/ism", "\n\n[img]$1[/img]\n\n", $body);
-               $body = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", "\n\n[audio]$1[/audio]\n\n", $body);
-               $body = preg_replace("/\[video\](.*?)\[\/video\]/ism", "\n\n[video]$1[/video]\n\n", $body);
+               $bbcode = preg_replace("/\[img\](.*?)\[\/img\]/ism", "\n\n[img]$1[/img]\n\n", $bbcode);
 
                do {
-                       $oldbody = $body;
-                       $body = str_replace(["\n\n\n"], ["\n\n"], $body);
-               } while ($oldbody != $body);
+                       $oldbbcode = $bbcode;
+                       $bbcode    = str_replace(["\n\n\n"], ["\n\n"], $bbcode);
+               } while ($oldbbcode != $bbcode);
+
+               return trim($bbcode);
+       }
+
+       /**
+        * Walk recursively through the HTML
+        *
+        * @param DOMElement $element
+        * @param integer $uri_id
+        * @param boolean $parse_structure
+        * @param array $callstack
+        * @param array $npf
+        * @param string $text
+        * @param array $formatting
+        * @return array
+        */
+       private static function routeChildren(DOMElement $element, int $uri_id, bool $parse_structure, array $callstack, array $npf = [], string $text = '', array $formatting = []): array
+       {
+               if ($parse_structure && $text) {
+                       list($npf, $text, $formatting) = self::addBlock($text, $formatting, $npf, $callstack);
+               }
+
+               $callstack[] = $element->nodeName;
+               $level       = self::getLevelByCallstack($callstack);
+
+               foreach ($element->childNodes as $child) {
+                       switch ($child->nodeName) {
+                               case 'b':
+                               case 'strong':
+                                       list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'bold', $callstack, $npf, $text, $formatting);
+                                       break;
+
+                               case 'i':
+                               case 'em':
+                                       list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'italic', $callstack, $npf, $text, $formatting);
+                                       break;
+
+                               case 's':
+                                       list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'strikethrough', $callstack, $npf, $text, $formatting);
+                                       break;
+
+                               case 'u':
+                               case 'span':
+                                       list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, '', $callstack, $npf, $text, $formatting);
+                                       break;
+
+                               case 'hr':
+                               case 'br':
+                                       if (!empty($text)) {
+                                               $text .= "\n";
+                                       }
+                                       break;
+
+                               case '#text':
+                                       $text .= $child->textContent;
+                                       break;
+
+                               case 'table':
+                               case 'summary':
+                                       // Ignore tables and spoilers
+                                       break;
+
+                               case 'a':
+                                       list($npf, $text, $formatting) = self::addInlineLink($child, $uri_id, $callstack, $npf, $text, $formatting);
+                                       break;
+
+                               case 'img':
+                                       $npf = self::addImageBlock($child, $uri_id, $level, $npf);
+                                       break;
+
+                               case 'audio':
+                               case 'video':
+                                       $npf = self::addMediaBlock($child, $uri_id, $level, $npf);
+                                       break;
+
+                               default:
+                                       list($npf, $text, $formatting) = self::routeChildren($child, $uri_id, true, $callstack, $npf, $text, $formatting);
+                                       break;
+                       }
+               }
+
+               if ($parse_structure && $text) {
+                       list($npf, $text, $formatting) = self::addBlock($text, $formatting, $npf, $callstack);
+               }
+               return [$npf, $text, $formatting];
+       }
+
+       /**
+        * Return the correct indent level
+        *
+        * @param array $callstack
+        * @return integer
+        */
+       private static function getLevelByCallstack(array $callstack): int
+       {
+               $level = 0;
+               foreach ($callstack as $entry) {
+                       if (in_array($entry, ['ol', 'ul', 'blockquote'])) {
+                               ++$level;
+                       }
+               }
+               return max(0, $level - 1);
+       }
+
+       /**
+        * Detect the subtype via the HTML element callstack
+        *
+        * @param array $callstack
+        * @param string $text
+        * @return string
+        */
+       private static function getSubTypeByCallstack(array $callstack, string $text): string
+       {
+               $subtype = '';
+               foreach ($callstack as $entry) {
+                       switch ($entry) {
+                               case 'ol':
+                                       $subtype = 'ordered-list-item';
+                                       break;
+
+                               case 'ul':
+                                       $subtype = 'unordered-list-item';
+                                       break;
 
-               return trim($body);
+                               case 'h1':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'h2':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'h3':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'h4':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'h5':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'h6':
+                                       $subtype = self::$heading_subtype[$entry];
+                                       break;
+
+                               case 'blockquote':
+                                       $subtype = mb_strlen($text) < 100 ? 'quote' : 'indented';
+                                       break;
+
+                               case 'pre':
+                                       $subtype = 'indented';
+                                       break;
+
+                               case 'code':
+                                       $subtype = 'chat';
+                                       break;
+                       }
+               }
+               return $subtype;
        }
 
-       static private function routeElements(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+       /**
+        * Add formatting for a text block
+        *
+        * @param DOMElement $element
+        * @param integer $uri_id
+        * @param string $type
+        * @param array $callstack
+        * @param array $npf
+        * @param string $text
+        * @param array $formatting
+        * @return array
+        */
+       private static function addFormatting(DOMElement $element, int $uri_id, string $type, array $callstack, array $npf, string $text, array $formatting): array
        {
-               switch ($child->tagName ?? '') {
-                       case 'blockquote':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'indented');
-                               break;
+               $start = mb_strlen($text);
 
-                       case 'h1':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
-                               break;
+               list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, false, $callstack, $npf, $text, $formatting);
 
-                       case 'h2':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
-                               break;
+               if (!empty($type)) {
+                       $formatting[] = [
+                               'start' => $start,
+                               'end'   => mb_strlen($text),
+                               'type'  => $type
+                       ];
+               }
+               return [$npf, $text, $formatting];
+       }
 
-                       case 'h3':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
-                               break;
+       /**
+        * Add an inline link for a text block
+        *
+        * @param DOMElement $element
+        * @param integer $uri_id
+        * @param array $callstack
+        * @param array $npf
+        * @param string $text
+        * @param array $formatting
+        * @return array
+        */
+       private static function addInlineLink(DOMElement $element, int $uri_id, array $callstack, array $npf, string $text, array $formatting): array
+       {
+               $start = mb_strlen($text);
 
-                       case 'h4':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
-                               break;
+               list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, false, $callstack, $npf, $text, $formatting);
 
-                       case 'h5':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
-                               break;
+               $attributes = [];
+               foreach ($element->attributes as $key => $attribute) {
+                       $attributes[$key] = trim($attribute->value);
+               }
+               if (!empty($attributes['href'])) {
+                       $formatting[] = [
+                               'start' => $start,
+                               'end'   => mb_strlen($text),
+                               'type'  => 'link',
+                               'url'   => $attributes['href']
+                       ];
+               }
+               return [$npf, $text, $formatting];
+       }
 
-                       case 'h6':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
-                               break;
+       /**
+        * Add a text block
+        *
+        * @param string $text
+        * @param array $formatting
+        * @param array $npf
+        * @param array $callstack
+        * @return array
+        */
+       private static function addBlock(string $text, array $formatting, array $npf, array $callstack): array
+       {
+               $block = [
+                       'type'    => 'text',
+                       'subtype' => '',
+                       'text'    => $text,
+               ];
 
-                       case 'ul':
-                               $npf = self::addListBlock($child, $uri_id, $npf, false, 0);
-                               break;
+               if (!empty($formatting)) {
+                       $block['formatting'] = $formatting;
+               }
 
-                       case 'ol':
-                               $npf = self::addListBlock($child, $uri_id, $npf, true, 0);
-                               break;
+               $level = self::getLevelByCallstack($callstack);
+               if ($level > 0) {
+                       $block['indent_level'] = $level;
+               }
 
-                       case 'hr':
-                       case 'br':
-                               break;
+               $subtype = self::getSubTypeByCallstack($callstack, $text);
+               if ($subtype) {
+                       $block['subtype'] = $subtype;
+               } else {
+                       unset($block['subtype']);
+               }
 
-                       case 'pre':
-                       case 'code':
-                               $npf = self::addTextBlock($child, $uri_id, $npf, 'indented');
-                               break;
+               $npf[] = $block;
+               return [$npf, '', []];
+       }
 
-                       case 'a':
-                               $npf = self::addMediaBlock($child, $uri_id, $npf);
-                               break;
+       /**
+        * Add a block for a preview picture
+        *
+        * @param array $media
+        * @param array $block
+        * @return array
+        */
+       private static function addPoster(array $media, array $block): array
+       {
+               $poster = [];
+               if (!empty($media['preview'])) {
+                       $poster['url'] = $media['preview'];
+               }
+               if (!empty($media['preview-width'])) {
+                       $poster['width'] = $media['preview-width'];
+               }
+               if (!empty($media['preview-height'])) {
+                       $poster['height'] = $media['preview-height'];
+               }
+               if (!empty($poster)) {
+                       $block['poster'] = [$poster];
+               }
+               return $block;
+       }
 
-                       case 'table':
-                               // $child->ownerDocument->saveHTML($child)
-                               break;
+       /**
+        * Add a link block from the HTML attachment of a given post uri-id
+        *
+        * @param integer $uri_id
+        * @param integer $level
+        * @param array $npf
+        * @return array
+        */
+       private static function addLinkBlockForUriId(int $uri_id, int $level, array $npf): array
+       {
+               foreach (Post\Media::getByURIId($uri_id, [Post\Media::HTML]) as $link) {
+                       $host = parse_url($link['url'], PHP_URL_HOST);
+                       if (in_array($host, ['www.youtube.com', 'youtu.be'])) {
+                               $block = [
+                                       'type'     => 'video',
+                                       'provider' => 'youtube',
+                                       'url'      => $link['url'],
+                               ];
+                       } elseif (in_array($host, ['vimeo.com'])) {
+                               $block = [
+                                       'type'     => 'video',
+                                       'provider' => 'vimeo',
+                                       'url'      => $link['url'],
+                               ];
+                       } elseif (in_array($host, ['open.spotify.com'])) {
+                               $block = [
+                                       'type'     => 'audio',
+                                       'provider' => 'spotify',
+                                       'url'      => $link['url'],
+                               ];
+                       } else {
+                               $block = [
+                                       'type' => 'link',
+                                       'url'  => $link['url'],
+                               ];
+                               if (!empty($link['name'])) {
+                                       $block['title'] = $link['name'];
+                               }
+                               if (!empty($link['description'])) {
+                                       $block['description'] = $link['description'];
+                               }
+                               if (!empty($link['author-name'])) {
+                                       $block['author'] = $link['author-name'];
+                               }
+                               if (!empty($link['publisher-name'])) {
+                                       $block['site_name'] = $link['publisher-name'];
+                               }
+                       }
 
-                       case 'img':
-                               $npf = self::addImageBlock($child, $uri_id, $npf);
-                               break;
+                       if ($level > 0) {
+                               $block['indent_level'] = $level;
+                       }
 
-                       default:
-                               $npf = self::addTextBlock($child, $uri_id, $npf);
-                               break;
+                       $npf[] = self::addPoster($link, $block);
                }
                return $npf;
        }
 
-       static private function addImageBlock(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+       /**
+        * Add an image block
+        *
+        * @param DOMElement $element
+        * @param integer $uri_id
+        * @param integer $level
+        * @param array $npf
+        * @return array
+        */
+       private static function addImageBlock(DOMElement $element, int $uri_id, int $level, array $npf): array
        {
                $attributes = [];
-               foreach ($child->attributes as $key => $attribute) {
-                       $attributes[$key] = $attribute->value;
+               foreach ($element->attributes as $key => $attribute) {
+                       $attributes[$key] = trim($attribute->value);
                }
                if (empty($attributes['src'])) {
                        return $npf;
                }
 
-               $entry = [
+               $block = [
                        'type'  => 'image',
                        'media' => [],
                ];
 
                if (!empty($attributes['alt'])) {
-                       $entry['alt_text'] = $attributes['alt'];
+                       $block['alt_text'] = $attributes['alt'];
                }
 
-               if (!empty($attributes['title']) && ($attributes['alt'] ?? '' != $attributes['title'])) {
-                       $entry['caption'] = $attributes['title'];
+               if (!empty($attributes['title']) && (($attributes['alt'] ?? '') != $attributes['title'])) {
+                       $block['caption'] = $attributes['title'];
                }
 
                $rid = Photo::ridFromURI($attributes['src']);
                if (!empty($rid)) {
                        $photos = Photo::selectToArray([], ['resource-id' => $rid]);
                        foreach ($photos as $photo) {
-                               $entry['media'][] = [
+                               $block['media'][] = [
                                        'type'   => $photo['type'],
                                        'url'    => str_replace('-0.', '-' . $photo['scale'] . '.', $attributes['src']),
                                        'width'  => $photo['width'],
@@ -200,43 +513,56 @@ class NPF
                                ];
                        }
                        if (empty($attributes['alt']) && !empty($photos[0]['desc'])) {
-                               $entry['alt_text'] = $photos[0]['desc'];
+                               $block['alt_text'] = $photos[0]['desc'];
                        }
                } elseif ($media = Post\Media::getByURL($uri_id, $attributes['src'], [Post\Media::IMAGE])) {
-                       $entry['media'][] = [
+                       $block['media'][] = [
                                'type'   => $media['mimetype'],
                                'url'    => $media['url'],
                                'width'  => $media['width'],
                                'height' => $media['height'],
                        ];
                        if (empty($attributes['alt']) && !empty($media['description'])) {
-                               $entry['alt_text'] = $media['description'];
+                               $block['alt_text'] = $media['description'];
                        }
                } else {
-                       $entry['media'][] = ['url' => $attributes['src']];
+                       $block['media'][] = ['url' => $attributes['src']];
                }
 
-               $npf[] = $entry;
+               if ($level > 0) {
+                       $block['indent_level'] = $level;
+               }
+
+               $npf[] = $block;
 
                return $npf;
        }
 
-       static private function addMediaBlock(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+       /**
+        * Add an audio or video block
+        *
+        * @param DOMElement $element
+        * @param integer $uri_id
+        * @param integer $level
+        * @param array $npf
+        * @return array
+        */
+       private static function addMediaBlock(DOMElement $element, int $uri_id, int $level, array $npf): array
        {
                $attributes = [];
-               foreach ($child->attributes as $key => $attribute) {
-                       $attributes[$key] = $attribute->value;
+               foreach ($element->attributes as $key => $attribute) {
+                       $attributes[$key] = trim($attribute->value);
                }
-               if (empty($attributes['href'])) {
+               if (empty($attributes['src'])) {
                        return $npf;
                }
 
-               $media = Post\Media::getByURL($uri_id, $attributes['href'], [Post\Media::AUDIO, Post\Media::VIDEO]);
+               $media = Post\Media::getByURL($uri_id, $attributes['src'], [Post\Media::AUDIO, Post\Media::VIDEO]);
                if (!empty($media)) {
                        switch ($media['type']) {
                                case Post\Media::AUDIO:
-                                       $entry = [
-                                               'type' => 'audio',
+                                       $block = [
+                                               'type'  => 'audio',
                                                'media' => [
                                                        'type' => $media['mimetype'],
                                                        'url'  => $media['url'],
@@ -244,222 +570,47 @@ class NPF
                                        ];
 
                                        if (!empty($media['name'])) {
-                                               $entry['title'] = $media['name'];
+                                               $block['title'] = $media['name'];
                                        } elseif (!empty($media['description'])) {
-                                               $entry['title'] = $media['description'];
+                                               $block['title'] = $media['description'];
                                        }
 
-                                       $npf[] = self::addPoster($media, $entry);
+                                       $block = self::addPoster($media, $block);
                                        break;
 
                                case Post\Media::VIDEO:
-                                       $entry = [
-                                               'type' => 'video',
+                                       $block = [
+                                               'type'  => 'video',
                                                'media' => [
                                                        'type' => $media['mimetype'],
                                                        'url'  => $media['url'],
                                                ]
                                        ];
 
-                                       $npf[] = self::addPoster($media, $entry);
+                                       $block = self::addPoster($media, $block);
                                        break;
                        }
                } else {
-                       $npf[] = [
-                               'type' => 'text',
-                               'text' => $child->textContent,
+                       $block = [
+                               'type'       => 'text',
+                               'text'       => $element->textContent,
                                'formatting' => [
-                                       'start' => 0,
-                                       'end'   => strlen($child->textContent),
-                                       'type'  => 'link',
-                                       'url'   => $attributes['href']
+                                       [
+                                               'start' => 0,
+                                               'end'   => mb_strlen($element->textContent),
+                                               'type'  => 'link',
+                                               'url'   => $attributes['src']
+                                       ]
                                ]
                        ];
                }
-               return $npf;
-       }
-
-       static private function addPoster(array $media, array $entry): array
-       {
-               $poster = [];
-               if (!empty($media['preview'])) {
-                       $poster['url'] = $media['preview'];
-               }
-               if (!empty($media['preview-width'])) {
-                       $poster['width'] = $media['preview-width'];
-               }
-               if (!empty($media['preview-height'])) {
-                       $poster['height'] = $media['preview-height'];
-               }
-               if (!empty($poster)) {
-                       $entry['poster'] = $poster;
-               }
-               return $entry;
-       }
-
-       static private function fetchText(DOMElement|DOMNode $child, array $text = ['text' => '', 'formatting' => []]): array
-       {
-               foreach ($child->childNodes as $node) {
-                       $start = strlen($text['text']);
-
-                       switch ($node->nodeName) {
-                               case 'b':
-                               case 'strong':
-                                       $type = 'bold';
-                                       break;
-
-                               case 'i':
-                               case 'em':
-                                       $type = 'italic';
-                                       break;
-       
-                               case 's':
-                                       $type = 'strikethrough';
-                                       break;
-                                                                                                               
-                               default:
-                                       $type = '';
-                                       break;
-                       }
-                       if ($node->nodeName == 'br') {
-                               $text['text'] .= "\n";
-                       } else {
-                               $text['text'] .= $node->textContent;
-                       }
-                       if (!empty($type)) {
-                               $text['formatting'][] = ['start' => $start, 'end' => strlen($text['text']), 'type' => $type];
-                       }
-               }
-               return $text;
-       }
-
-       static private function addTextBlock(DOMElement|DOMNode $child, int $uri_id, array $npf, string $subtype = ''): array
-       {
-               if (empty($subtype) && (($child->childElementCount) ?? 0 == 1) && ($child->textContent == $child->firstChild->textContent)) {
-                       return self::routeElements($child->firstChild, $uri_id, $npf);
-               }
-
-               $element = ['type' => 'text'];
-
-               if (!empty($subtype)) {
-                       $element['subtype'] = $subtype;
-               }
-
-               $text = self::fetchText($child);
-
-               $element['text']       = $text['text'];
-               $element['formatting'] = $text['formatting'];
-
-               if (empty($subtype)) {
-                       switch ($child->tagName ?? '') {
-                               case 'b':
-                               case 'strong':
-                                       $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'bold'];
-                                       break;
-
-                               case 'i':
-                               case 'em':
-                                       $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'italic'];
-                                       break;
-
-                               case 's':
-                                       $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'strikethrough'];
-                                       break;
-
-                               case 'span':
-                               case 'p':
-                               case 'div':
-                               case 'details';
-                               case '':
-                                       break;
-                               default:
-                                       print_r($element);
-                                       die($child->tagName . "\n");
-                                       break;
-                       }
-               }
 
-               if (empty($element['formatting'])) {
-                       unset($element['formatting']);
+               if ($level > 0) {
+                       $block['indent_level'] = $level;
                }
 
-               $npf[] = $element;
+               $npf[] = $block;
 
                return $npf;
        }
-
-       static private function addListBlock(DOMElement|DOMNode $child, int $uri_id, array $npf, bool $ordered, int $level): array
-       {
-               foreach ($child->childNodes as $node) {
-                       switch ($node->nodeName) {
-                               case 'ul':
-                                       $npf = self::addListBlock($node, $uri_id, $npf, false, $level++);
-                               case 'ol':
-                                       $npf = self::addListBlock($node, $uri_id, $npf, true, $level++);
-                               case 'li':
-                                       $text = self::fetchText($node);
-
-                                       $entry = [
-                                               'type'    => 'text',
-                                               'subtype' => $ordered ? 'ordered-list-item' : 'unordered-list-item',
-                                               'text'    => $text['text']
-                                       ];
-                                       if ($level > 0) {
-                                               $entry['indent_level'] = $level;
-                                       }
-                                       if (!empty($text['formatting'])) {
-                                               $entry['formatting'] = $text['formatting'];
-                                       }
-                                       $npf[] = $entry;
-                       }
-               }
-
-               return $npf;
-       }
-
-       static private function addLinkBlock(int $uri_id, array $npf): array
-       {
-               foreach (Post\Media::getByURIId($uri_id, [Post\Media::HTML]) as $link) {
-                       $host = parse_url($link['url'], PHP_URL_HOST);
-                       if (in_array($host, ['www.youtube.com', 'youtu.be'])) {
-                               $entry = [
-                                       'type'     => 'video',
-                                       'provider' => 'youtube',
-                                       'url'      => $link['url'],
-                               ];
-                       } elseif (in_array($host, ['vimeo.com'])) {
-                               $entry = [
-                                       'type'     => 'video',
-                                       'provider' => 'vimeo',
-                                       'url'      => $link['url'],
-                               ];
-                       } elseif (in_array($host, ['open.spotify.com'])) {
-                               $entry = [
-                                       'type'     => 'audio',
-                                       'provider' => 'spotify',
-                                       'url'      => $link['url'],
-                               ];
-                       } else {
-                               $entry = [
-                                       'type' => 'link',
-                                       'url'  => $link['url'],
-                               ];
-                               if (!empty($link['name'])) {
-                                       $entry['title'] = $link['name'];
-                               }
-                               if (!empty($link['description'])) {
-                                       $entry['description'] = $link['description'];
-                               }
-                               if (!empty($link['author-name'])) {
-                                       $entry['author'] = $link['author-name'];
-                               }
-                               if (!empty($link['publisher-name'])) {
-                                       $entry['site_name'] = $link['publisher-name'];
-                               }
-                       }
-
-                       $npf[] = self::addPoster($link, $entry);
-               }
-               return $npf;
-       }
 }