NodeExtensions
Useful methods for node objects.
using AngleSharp.Dom;
using AngleSharp.Dom.Collections;
using AngleSharp.Dom.Html;
using System.Collections.Generic;
using System.Linq;
namespace AngleSharp.Extensions
{
internal static class NodeExtensions
{
public static INode GetRoot(this INode node)
{
if (node.Parent == null)
return node;
return node.Parent.GetRoot();
}
public static NodeList CreateChildren(this INode node)
{
if (!node.IsEndPoint())
return new NodeList();
return NodeList.Empty;
}
public static bool IsEndPoint(this INode node)
{
NodeType nodeType = node.NodeType;
if (nodeType != NodeType.Document && nodeType != NodeType.DocumentFragment)
return nodeType != NodeType.Element;
return false;
}
public static bool IsInsertable(this INode node)
{
NodeType nodeType = node.NodeType;
if (nodeType != NodeType.Element && nodeType != NodeType.Comment && nodeType != NodeType.Text && nodeType != NodeType.ProcessingInstruction && nodeType != NodeType.DocumentFragment)
return nodeType == NodeType.DocumentType;
return true;
}
public static Url HyperReference(this INode node, string url)
{
if (url == null)
return null;
return new Url(node.BaseUrl, url);
}
public static bool IsDescendantOf(this INode node, INode parent)
{
if (node.Parent == null)
return false;
if (node.Parent == parent)
return true;
return node.Parent.IsDescendantOf(parent);
}
public static IEnumerable<INode> GetDescendants(this INode parent)
{
return parent.GetDescendantsAndSelf().Skip(1);
}
public static IEnumerable<INode> GetDescendantsAndSelf(this INode parent)
{
Stack<INode> stack = new Stack<INode>();
stack.Push(parent);
while (stack.Count > 0) {
INode next = stack.Pop();
yield return next;
int num = next.ChildNodes.Length;
while (num > 0) {
stack.Push(next.ChildNodes[--num]);
}
}
}
public static bool IsInclusiveDescendantOf(this INode node, INode parent)
{
if (node != parent)
return node.IsDescendantOf(parent);
return true;
}
public static bool IsAncestorOf(this INode parent, INode node)
{
return node.IsDescendantOf(parent);
}
public static IEnumerable<INode> GetAncestors(this INode node)
{
while (true) {
INode parent;
node = (parent = node.Parent);
if (parent == null)
break;
yield return node;
}
}
public static IEnumerable<INode> GetInclusiveAncestors(this INode node)
{
INode parent;
do {
yield return node;
node = (parent = node.Parent);
} while (parent != null);
}
public static bool IsInclusiveAncestorOf(this INode parent, INode node)
{
if (node != parent)
return node.IsDescendantOf(parent);
return true;
}
public static T GetAncestor<T>(this INode node) where T : INode
{
while ((node = node.Parent) != null) {
if (node is T)
return (T)node;
}
return default(T);
}
public static bool HasDataListAncestor(this INode child)
{
return child.Ancestors<IHtmlDataListElement>().Any();
}
public static bool IsSiblingOf(this INode node, INode element)
{
return node?.Parent == element.Parent;
}
public static int Index(this INode node)
{
return node.Parent.IndexOf(node);
}
public static int IndexOf(this INode parent, INode node)
{
int num = 0;
if (parent != null) {
foreach (INode childNode in parent.ChildNodes) {
if (childNode == node)
return num;
num++;
}
}
return -1;
}
public static bool IsPreceding(this INode before, INode after)
{
Queue<INode> queue = new Queue<INode>(before.GetInclusiveAncestors());
Queue<INode> queue2 = new Queue<INode>(after.GetInclusiveAncestors());
int num = queue2.Count - queue.Count;
if (num != 0) {
while (queue.Count > queue2.Count) {
queue.Dequeue();
}
while (queue2.Count > queue.Count) {
queue2.Dequeue();
}
if (IsCurrentlySame(queue2, queue))
return num > 0;
}
while (queue.Count > 0) {
before = queue.Dequeue();
after = queue2.Dequeue();
if (IsCurrentlySame(queue2, queue))
return before.Index() < after.Index();
}
return false;
}
public static bool IsFollowing(this INode after, INode before)
{
return before.IsPreceding(after);
}
public static INode GetAssociatedHost(this INode node)
{
if (node is IDocumentFragment) {
IDocument owner = node.Owner;
if (owner == null)
return null;
return owner.All.OfType<IHtmlTemplateElement>().FirstOrDefault((IHtmlTemplateElement m) => m.Content == node);
}
return null;
}
public static bool IsHostIncludingInclusiveAncestor(this INode parent, INode node)
{
if (!parent.IsInclusiveAncestorOf(node)) {
INode associatedHost = node.GetRoot().GetAssociatedHost();
if (associatedHost != null)
return parent.IsInclusiveAncestorOf(associatedHost);
return false;
}
return true;
}
public static void EnsurePreInsertionValidity(this INode parent, INode node, INode child)
{
if (parent.IsEndPoint() || node.IsHostIncludingInclusiveAncestor(parent))
throw new DomException(DomError.HierarchyRequest);
if (child != null && child.Parent != parent)
throw new DomException(DomError.NotFound);
if (!(node is IElement) && !(node is ICharacterData) && !(node is IDocumentType) && !(node is IDocumentFragment))
throw new DomException(DomError.HierarchyRequest);
IDocument document = parent as IDocument;
if (document != null) {
bool flag = false;
switch (node.NodeType) {
case NodeType.Element:
flag = (document.DocumentElement != null || child is IDocumentType || child.IsFollowedByDoctype());
break;
case NodeType.DocumentFragment: {
int elementCount = node.GetElementCount();
flag = (elementCount > 1 || node.HasTextNodes() || (elementCount == 1 && document.DocumentElement != null) || child is IDocumentType || child.IsFollowedByDoctype());
break;
}
case NodeType.DocumentType:
flag = (document.Doctype != null || (child != null && child.IsPrecededByElement()) || (child == null && document.DocumentElement != null));
break;
case NodeType.Text:
flag = true;
break;
}
if (flag)
throw new DomException(DomError.HierarchyRequest);
} else if (node is IDocumentType) {
throw new DomException(DomError.HierarchyRequest);
}
}
public static INode PreInsert(this INode parent, INode node, INode child)
{
Node obj = parent as Node;
Node node2 = node as Node;
if (obj == null)
throw new DomException(DomError.NotSupported);
parent.EnsurePreInsertionValidity(node, child);
Node node3 = child as Node;
if (node3 == node)
node3 = node2.NextSibling;
(parent.Owner ?? (parent as IDocument)).AdoptNode(node);
obj.InsertBefore(node2, node3, false);
return node;
}
public static INode PreRemove(this INode parent, INode child)
{
Node obj = parent as Node;
if (obj == null)
throw new DomException(DomError.NotSupported);
if (child == null || child.Parent != parent)
throw new DomException(DomError.NotFound);
obj.RemoveChild(child as Node, false);
return child;
}
public static bool HasTextNodes(this INode node)
{
return node.ChildNodes.OfType<IText>().Any();
}
public static bool IsFollowedByDoctype(this INode child)
{
if (child != null) {
bool flag = true;
foreach (INode childNode in child.Parent.ChildNodes) {
if (flag)
flag = (childNode != child);
else if (childNode.NodeType == NodeType.DocumentType) {
return true;
}
}
}
return false;
}
public static bool IsPrecededByElement(this INode child)
{
foreach (INode childNode in child.Parent.ChildNodes) {
if (childNode == child)
break;
if (childNode.NodeType == NodeType.Element)
return true;
}
return false;
}
public static int GetElementCount(this INode parent)
{
int num = 0;
foreach (INode childNode in parent.ChildNodes) {
if (childNode.NodeType == NodeType.Element)
num++;
}
return num;
}
public static TNode FindChild<TNode>(this INode parent) where TNode : class, INode
{
if (parent != null) {
for (int i = 0; i < parent.ChildNodes.Length; i++) {
TNode val = parent.ChildNodes[i] as TNode;
if (val != null)
return val;
}
}
return null;
}
public static TNode FindDescendant<TNode>(this INode parent) where TNode : class, INode
{
if (parent != null) {
for (int i = 0; i < parent.ChildNodes.Length; i++) {
INode node = parent.ChildNodes[i];
TNode val = (node as TNode) ?? node.FindDescendant<TNode>();
if (val != null)
return val;
}
}
return null;
}
public static IElement GetAssignedSlot(this IShadowRoot root, string name)
{
return root.GetDescendants().OfType<IHtmlSlotElement>().FirstOrDefault((IHtmlSlotElement m) => m.Name.Is(name));
}
private static bool IsCurrentlySame(Queue<INode> after, Queue<INode> before)
{
if (after.Count > 0 && before.Count > 0)
return after.Peek() == before.Peek();
return false;
}
}
}