*/
protected array $toCleanup = [];
+ /**
+ * @param Closure(PageIncludeTag $tag): PageContent $pageContentForId
+ */
public function __construct(
- protected string $pageHtml,
+ protected HtmlDocument $doc,
protected Closure $pageContentForId,
) {
}
/**
* Parse out the include tags.
+ * Returns the count of new content DOM nodes added to the document.
*/
- public function parse(): string
+ public function parse(): int
{
- $doc = new HtmlDocument($this->pageHtml);
-
- $tags = $this->locateAndIsolateIncludeTags($doc);
+ $nodesAdded = 0;
+ $tags = $this->locateAndIsolateIncludeTags();
foreach ($tags as $tag) {
- $htmlContent = $this->pageContentForId->call($this, $tag->getPageId());
- $content = new PageIncludeContent($htmlContent, $tag);
+ /** @var PageIncludeContent $content */
+ $content = $this->pageContentForId->call($this, $tag);
if (!$content->isInline()) {
$parentP = $this->getParentParagraph($tag->domNode);
}
}
- $this->replaceNodeWithNodes($tag->domNode, $content->toDomNodes());
+ $replacementNodes = $content->toDomNodes();
+ $nodesAdded += count($replacementNodes);
+ $this->replaceNodeWithNodes($tag->domNode, $replacementNodes);
}
$this->cleanup();
- return $doc->getBodyInnerHtml();
+ return $nodesAdded;
}
/**
* own nodes in the DOM for future targeted manipulation.
* @return PageIncludeTag[]
*/
- protected function locateAndIsolateIncludeTags(HtmlDocument $doc): array
+ protected function locateAndIsolateIncludeTags(): array
{
- $includeHosts = $doc->queryXPath("//body//*[text()[contains(., '{{@')]]");
+ $includeHosts = $this->doc->queryXPath("//*[text()[contains(., '{{@')]]");
$includeTags = [];
/** @var DOMNode $node */
- /** @var DOMNode $childNode */
foreach ($includeHosts as $node) {
+ /** @var DOMNode $childNode */
foreach ($node->childNodes as $childNode) {
if ($childNode->nodeName === '#text') {
array_push($includeTags, ...$this->splitTextNodesAtTags($childNode));
if ($currentOffset < $tagStartOffset) {
$previousText = substr($text, $currentOffset, $tagStartOffset - $currentOffset);
- $textNode->parentNode->insertBefore(new DOMText($previousText), $textNode);
+ $textNode->parentNode->insertBefore($this->doc->createTextNode($previousText), $textNode);
}
- $node = $textNode->parentNode->insertBefore(new DOMText($tagOuterContent), $textNode);
+ $node = $textNode->parentNode->insertBefore($this->doc->createTextNode($tagOuterContent), $textNode);
$includeTags[] = new PageIncludeTag($tagInnerContent, $node);
$currentOffset = $tagStartOffset + strlen($tagOuterContent);
}
foreach ($replacements as $replacement) {
if ($replacement->ownerDocument !== $targetDoc) {
- $replacement = $targetDoc->adoptNode($replacement);
+ $replacement = $targetDoc->importNode($replacement, true);
}
$toReplace->parentNode->insertBefore($replacement, $toReplace);
$parentText = $parent->textContent;
$tagPos = strpos($parentText, $tag->tagContent);
$before = $tagPos < (strlen($parentText) / 2);
+ $this->toCleanup[] = $tag->domNode->parentNode;
if ($before) {
$parent->parentNode->insertBefore($tag->domNode, $parent);
$parentNode->parentNode->insertBefore($parentClone, $parentNode);
$parentClone->removeAttribute('id');
- /** @var DOMNode $child */
for ($i = 0; $i < $splitPos; $i++) {
+ /** @var DOMNode $child */
$child = $children[$i];
$parentClone->appendChild($child);
}
return $parent;
}
- $parent = $parent->parentElement;
+ $parent = $parent->parentNode;
} while ($parent !== null);
return null;
{
foreach ($this->toCleanup as $element) {
$element->normalize();
- if ($element->parentNode && !$element->hasChildNodes()) {
- $element->parentNode->removeChild($element);
+ while ($element->parentNode && !$element->hasChildNodes()) {
+ $parent = $element->parentNode;
+ $parent->removeChild($element);
+ $element = $parent;
}
}
}