- Модуль: main
- Путь к файлу: ~/bitrix/modules/main/lib/web/dom/htmlparser.php
- Класс: BitrixMainWebDOMHtmlParser
- Вызов: HtmlParser::getNextNode
protected function getNextNode($tag, Node $parentNode)
{
$node = null;
$isSingleTag = true;
static $tagsWithoutClose = array('INPUT'=>1, 'IMG'=>1, 'BR'=>1, 'HR'=>1, 'META'=>1, 'AREA'=>1, 'BASE'=>1, 'COL'=>1, 'EMBED'=>1, 'KEYGEN'=>1, 'LINK'=>1, 'PARAM'=>1, 'SOURCE'=>1, 'TRACK'=>1, 'WBR'=>1);
$tagsCantHaveNestedTags = array();
$document = $parentNode->getOwnerDocument();
if($parentNode->getNodeType() === Node::COMMENT_NODE)
{
$commentClosePosition = mb_strpos($tag, '-->');
if($commentClosePosition !== false)
{
$clean = mb_substr($tag, 0, $commentClosePosition);
$parentNode->setNodeValue($parentNode->getNodeValue() . $clean);
$parentNode->bxNodeFoundCloseTag = true;
$tag = mb_substr($tag, $commentClosePosition + 3);
if(!$tag)
{
return $parentNode->getParentNode();
}
else
{
$parentNode = $parentNode->getParentNode();
}
}
else
{
$parentNode->setNodeValue($parentNode->getNodeValue() . $tag);
return $parentNode;
}
}
elseif(in_array($parentNode->getNodeName(), $this->tagsMustBeClosed))
{
if(mb_strtoupper(mb_substr($tag, -9)) == ''.$parentNode->getNodeName().'>')
{
$parentNode->bxNodeFoundCloseTag = true;
$parentNode = $parentNode->getParentNode();
}
else
{
$firstChild = $parentNode->getFirstChild();
if(!$firstChild)
{
$parentNode->appendChild($document->createTextNode($tag));
}
else
{
$firstChild->setNodeValue($firstChild->getNodeValue() . $tag);
}
$parentNode->bxNodeFoundCloseTag = false;
return $parentNode;
}
}
if(mb_substr($tag, 0, 2) === '')
{
// closed tag
//TODO: find closest opened parent with same nodeName and return it
$cleaned = mb_strtoupper(mb_substr($tag, 2, -mb_strlen('>')));
$searchableNode = $parentNode;
$isSearchableNodeFound = false;
$unclosedNodes = array();
do
{
if(!$searchableNode->bxNodeFoundCloseTag)
{
$unclosedNodes[] = $searchableNode;
}
if($searchableNode->getNodeName() === $cleaned)
{
$isSearchableNodeFound = true;
break;
}
}while($searchableNode = $searchableNode->getParentNode());
if($isSearchableNodeFound)
{
foreach($unclosedNodes as $unclosedNode)
{
/* @var $unclosedNode Node */
if(in_array($unclosedNode->getNodeName(), $tagsCantHaveNestedTags))
{
if($unclosedNode->hasChildNodes())
{
foreach ($unclosedNode->getChildNodesArray() as $childNode)
{
$unclosedNode->getParentNode()->appendChild($unclosedNode->removeChild($childNode));
}
}
}
$unclosedNode->bxNodeFoundCloseTag = true;
}
return $searchableNode->getParentNode();
}
else
{
if(false)
{
throw new DomException('Parser error. Find close tag, but can not find open tag ' . $cleaned);
}
else
{
if ($parentNode->getParentNode())
{
$parentNode->getParentNode()->bxNodeFoundCloseTag = true;
}
return $parentNode;
}
}
}
elseif(mb_substr($tag, 0, 4) === '')
{
$cleaned = mb_substr($cleaned, 0, -3);
$parentNode->bxNodeFoundCloseTag = true;
}
else
{
$isSingleTag = false;
}
//$parentNode->bxNodeFoundCloseTag = false;
$node = $document->createComment($cleaned);
}
elseif(mb_substr($tag, 0, 1) === '<')
{
// Element
if(mb_substr($tag, -2) === '/>')
{
// empty tag
$cleaned = mb_substr($tag, 1, -2);
$bxNodeWithCloseTag = false;
}
else
{
$cleaned = mb_substr($tag, 1, -1);
$isSingleTag = false;
$bxNodeWithCloseTag = true;
}
$list = $this->parseElement($cleaned);
$isDocType = mb_substr($list['NAME'], 0, mb_strlen('!DOCTYPE')) === '!DOCTYPE';
if(isset($tagsWithoutClose[$list['NAME']]) || $isDocType)
{
$bxNodeWithCloseTag = false;
$isSingleTag = true;
}
if($isDocType)
{
$list = $this->parseDocType($cleaned);
//TODO: set doctype fields
}
else
{
$node = $document->createElement($list['NAME']);
foreach($list['ATTRIBUTES'] as $attrName => $attrValue)
{
$nodeAttr = $document->createAttribute($attrName, $attrValue);
$node->setAttributeNode($nodeAttr);
}
$node->bxNodeWithCloseTag = $bxNodeWithCloseTag;
}
}
else
{
// Text
$cleaned = html_entity_decode($tag, ENT_QUOTES, (defined("BX_UTF") ? "UTF-8" : "ISO-8859-1"));
$node = $document->createTextNode($cleaned);
}
if($node && $parentNode)
{
$parentNode->appendChild($node);
if(!$isSingleTag)
{
return $node;
}
}
return $parentNode;
}