AngleSharp by AngleSharp

<PackageReference Include="AngleSharp" Version="0.9.11" />

 Range

sealed class Range : IRange
A DOM range to gather DOM tree information.
using AngleSharp.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AngleSharp.Dom.Collections { internal sealed class Range : IRange { private struct Boundary : IEquatable<Boundary> { public INode Node; public int Offset; public INode ChildAtOffset { get { if (Node.ChildNodes.Length <= Offset) return null; return Node.ChildNodes[Offset]; } } public static bool operator >(Boundary a, Boundary b) { return false; } public static bool operator <(Boundary a, Boundary b) { return false; } public bool Equals(Boundary other) { if (Node == other.Node) return Offset == other.Offset; return false; } public RangePosition CompareTo(Boundary other) { if (this < other) return RangePosition.Before; if (this > other) return RangePosition.After; return RangePosition.Equal; } } private Boundary _start; private Boundary _end; public INode Root => _start.Node.GetRoot(); public IEnumerable<INode> Nodes => CommonAncestor.GetElements<INode>(true, Intersects); public INode Head => _start.Node; public int Start => _start.Offset; public INode Tail => _end.Node; public int End => _end.Offset; public bool IsCollapsed => _start.Node == _end.Node; public INode CommonAncestor { get { INode node = Head; while (node != null && !Tail.Contains(node)) { node = node.Parent; } return node; } } public Range(IDocument document) { Boundary boundary = new Boundary { Offset = 0, Node = document }; _start = boundary; boundary = new Boundary { Offset = 0, Node = document }; _end = boundary; } private Range(Boundary start, Boundary end) { _start = start; _end = end; } public void StartWith(INode refNode, int offset) { if (refNode == null) throw new ArgumentNullException("refNode"); if (refNode.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); if (offset > refNode.ChildNodes.Length) throw new DomException(DomError.IndexSizeError); Boundary boundary = default(Boundary); boundary.Node = refNode; boundary.Offset = offset; Boundary boundary2 = boundary; if (boundary2 > _end || Root != refNode.GetRoot()) _start = boundary2; } public void EndWith(INode refNode, int offset) { if (refNode == null) throw new ArgumentNullException("refNode"); if (refNode.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); if (offset > refNode.ChildNodes.Length) throw new DomException(DomError.IndexSizeError); Boundary boundary = default(Boundary); boundary.Node = refNode; boundary.Offset = offset; Boundary boundary2 = boundary; if (boundary2 < _start || Root != refNode.GetRoot()) _end = boundary2; } public void StartBefore(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); INode parent = refNode.Parent; if (parent == null) throw new DomException(DomError.InvalidNodeType); _start = new Boundary { Node = parent, Offset = parent.ChildNodes.Index(refNode) }; } public void EndBefore(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); INode parent = refNode.Parent; if (parent == null) throw new DomException(DomError.InvalidNodeType); _end = new Boundary { Node = parent, Offset = parent.ChildNodes.Index(refNode) }; } public void StartAfter(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); INode parent = refNode.Parent; if (parent == null) throw new DomException(DomError.InvalidNodeType); _start = new Boundary { Node = parent, Offset = parent.ChildNodes.Index(refNode) + 1 }; } public void EndAfter(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); INode parent = refNode.Parent; if (parent == null) throw new DomException(DomError.InvalidNodeType); _end = new Boundary { Node = parent, Offset = parent.ChildNodes.Index(refNode) + 1 }; } public void Collapse(bool toStart) { if (toStart) _end = _start; else _start = _end; } public void Select(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); INode parent = refNode.Parent; if (parent == null) throw new DomException(DomError.InvalidNodeType); int num = parent.ChildNodes.Index(refNode); Boundary boundary = _start = new Boundary { Node = parent, Offset = num }; boundary = (_end = new Boundary { Node = parent, Offset = num + 1 }); } public void SelectContent(INode refNode) { if (refNode == null) throw new ArgumentNullException("refNode"); if (refNode.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); int length = refNode.ChildNodes.Length; Boundary boundary = _start = new Boundary { Node = refNode, Offset = 0 }; boundary = (_end = new Boundary { Node = refNode, Offset = length }); } public void ClearContent() { if (!_start.Equals(_end)) { Boundary boundary = default(Boundary); Boundary start = _start; Boundary end = _end; if (end.Node == start.Node && start.Node is ICharacterData) { int offset = start.Offset; ICharacterData obj = (ICharacterData)start.Node; int count = end.Offset - start.Offset; obj.Replace(offset, count, string.Empty); } else { INode[] array = (from m in Nodes where !Intersects(m.Parent) select m).ToArray(); if (!start.Node.IsInclusiveAncestorOf(end.Node)) { INode node = start.Node; while (node.Parent != null && node.Parent.IsInclusiveAncestorOf(end.Node)) { node = node.Parent; } Boundary boundary2 = default(Boundary); boundary2.Node = node.Parent; boundary2.Offset = node.Parent.ChildNodes.Index(node) + 1; boundary = boundary2; } else boundary = start; if (start.Node is ICharacterData) { int offset2 = start.Offset; ICharacterData obj2 = (ICharacterData)start.Node; int count2 = end.Offset - start.Offset; obj2.Replace(offset2, count2, string.Empty); } INode[] array2 = array; foreach (INode node2 in array2) { node2.Parent.RemoveChild(node2); } if (end.Node is ICharacterData) { int offset3 = 0; ICharacterData obj3 = (ICharacterData)end.Node; int offset4 = end.Offset; obj3.Replace(offset3, offset4, string.Empty); } _start = boundary; _end = boundary; } } } public IDocumentFragment ExtractContent() { IDocumentFragment documentFragment = _start.Node.Owner.CreateDocumentFragment(); if (_start.Equals(_end)) return documentFragment; Boundary boundary = _start; Boundary start = _start; Boundary end = _end; if (start.Node == end.Node && _start.Node is ICharacterData) { ICharacterData characterData = (ICharacterData)start.Node; int offset = start.Offset; int count = end.Offset - start.Offset; ICharacterData characterData2 = (ICharacterData)characterData.Clone(true); characterData2.Data = characterData.Substring(offset, count); documentFragment.AppendChild(characterData2); characterData.Replace(offset, count, string.Empty); return documentFragment; } INode node = start.Node; while (!node.IsInclusiveAncestorOf(end.Node)) { node = node.Parent; } INode node2 = (!start.Node.IsInclusiveAncestorOf(end.Node)) ? node.GetElements<INode>(true, IsPartiallyContained).FirstOrDefault() : null; INode node3 = (!end.Node.IsInclusiveAncestorOf(start.Node)) ? node.GetElements<INode>(true, IsPartiallyContained).LastOrDefault() : null; List<INode> list = node.GetElements<INode>(true, Intersects).ToList(); if (list.OfType<IDocumentType>().Any()) throw new DomException(DomError.HierarchyRequest); Boundary boundary2; if (!start.Node.IsInclusiveAncestorOf(end.Node)) { INode node4 = start.Node; while (node4.Parent != null && !node4.IsInclusiveAncestorOf(end.Node)) { node4 = node4.Parent; } boundary2 = default(Boundary); boundary2.Node = node4; boundary2.Offset = node4.Parent.ChildNodes.Index(node4) + 1; boundary = boundary2; } if (node2 is ICharacterData) { ICharacterData characterData3 = (ICharacterData)start.Node; int offset2 = start.Offset; int count2 = characterData3.Length - start.Offset; ICharacterData characterData4 = (ICharacterData)characterData3.Clone(true); characterData4.Data = characterData3.Substring(offset2, count2); documentFragment.AppendChild(characterData4); characterData3.Replace(offset2, count2, string.Empty); } else if (node2 != null) { INode child = node2.Clone(true); documentFragment.AppendChild(child); Boundary start2 = start; boundary2 = new Boundary { Node = node2, Offset = node2.ChildNodes.Length }; IDocumentFragment child2 = new Range(start2, boundary2).ExtractContent(); documentFragment.AppendChild(child2); } foreach (INode item in list) { documentFragment.AppendChild(item); } if (node3 is ICharacterData) { ICharacterData characterData5 = (ICharacterData)end.Node; ICharacterData characterData6 = (ICharacterData)characterData5.Clone(true); characterData6.Data = characterData5.Substring(0, end.Offset); documentFragment.AppendChild(characterData6); characterData5.Replace(0, end.Offset, string.Empty); } else if (node3 != null) { INode child3 = node3.Clone(true); documentFragment.AppendChild(child3); boundary2 = default(Boundary); boundary2.Node = node3; boundary2.Offset = 0; IDocumentFragment child4 = new Range(boundary2, end).ExtractContent(); documentFragment.AppendChild(child4); } _start = boundary; _end = boundary; return documentFragment; } public IDocumentFragment CopyContent() { IDocumentFragment documentFragment = _start.Node.Owner.CreateDocumentFragment(); if (_start.Equals(_end)) return documentFragment; Boundary start = _start; Boundary end = _end; if (start.Node == end.Node && _start.Node is ICharacterData) { ICharacterData characterData = (ICharacterData)start.Node; int offset = start.Offset; int count = end.Offset - start.Offset; ICharacterData characterData2 = (ICharacterData)characterData.Clone(true); characterData2.Data = characterData.Substring(offset, count); documentFragment.AppendChild(characterData2); return documentFragment; } INode node = start.Node; while (!node.IsInclusiveAncestorOf(end.Node)) { node = node.Parent; } INode node2 = (!start.Node.IsInclusiveAncestorOf(end.Node)) ? node.GetElements<INode>(true, IsPartiallyContained).FirstOrDefault() : null; INode node3 = (!end.Node.IsInclusiveAncestorOf(start.Node)) ? node.GetElements<INode>(true, IsPartiallyContained).LastOrDefault() : null; List<INode> list = node.GetElements<INode>(true, Intersects).ToList(); if (list.OfType<IDocumentType>().Any()) throw new DomException(DomError.HierarchyRequest); Boundary boundary; if (node2 is ICharacterData) { ICharacterData characterData3 = (ICharacterData)start.Node; int offset2 = start.Offset; int count2 = characterData3.Length - start.Offset; ICharacterData characterData4 = (ICharacterData)characterData3.Clone(true); characterData4.Data = characterData3.Substring(offset2, count2); documentFragment.AppendChild(characterData4); } else if (node2 != null) { INode child = node2.Clone(true); documentFragment.AppendChild(child); Boundary start2 = start; boundary = new Boundary { Node = node2, Offset = node2.ChildNodes.Length }; IDocumentFragment child2 = new Range(start2, boundary).CopyContent(); documentFragment.AppendChild(child2); } foreach (INode item in list) { documentFragment.AppendChild(item.Clone(true)); } if (node3 is ICharacterData) { ICharacterData characterData5 = (ICharacterData)end.Node; ICharacterData characterData6 = (ICharacterData)characterData5.Clone(true); characterData6.Data = characterData5.Substring(0, end.Offset); documentFragment.AppendChild(characterData6); } else if (node3 != null) { INode child3 = node3.Clone(true); documentFragment.AppendChild(child3); boundary = default(Boundary); boundary.Node = node3; boundary.Offset = 0; IDocumentFragment child4 = new Range(boundary, end).CopyContent(); documentFragment.AppendChild(child4); } return documentFragment; } public void Insert(INode node) { if (node == null) throw new ArgumentNullException("node"); INode node2 = _start.Node; NodeType nodeType = node2.NodeType; bool flag = nodeType == NodeType.Text; if (nodeType == NodeType.ProcessingInstruction || nodeType == NodeType.Comment || (flag && node2.Parent == null)) throw new DomException(DomError.HierarchyRequest); INode node3 = flag ? node2 : _start.ChildAtOffset; INode node4 = (node3 == null) ? node2 : node3.Parent; node4.EnsurePreInsertionValidity(node, node3); if (flag) { node3 = ((IText)node2).Split(_start.Offset); node4 = node3.Parent; } if (node == node3) node3 = node3.NextSibling; node.Parent?.RemoveChild(node); int num = (node3 == null) ? node4.ChildNodes.Length : node4.ChildNodes.Index(node3); num += ((node.NodeType != NodeType.DocumentFragment) ? 1 : node.ChildNodes.Length); node4.PreInsert(node, node3); if (_start.Equals(_end)) _end = new Boundary { Node = node4, Offset = num }; } public void Surround(INode newParent) { if (newParent == null) throw new ArgumentNullException("newParent"); if (Nodes.Any(delegate(INode m) { if (m.NodeType != NodeType.Text) return IsPartiallyContained(m); return false; })) throw new DomException(DomError.InvalidState); NodeType nodeType = newParent.NodeType; if (nodeType == NodeType.Document || nodeType == NodeType.DocumentType || nodeType == NodeType.DocumentFragment) throw new DomException(DomError.InvalidNodeType); IDocumentFragment node = ExtractContent(); while (newParent.HasChildNodes) { newParent.RemoveChild(newParent.FirstChild); } Insert(newParent); newParent.PreInsert(node, null); Select(newParent); } public IRange Clone() { return new Range(_start, _end); } public void Detach() { } public bool Contains(INode node, int offset) { if (node == null) throw new ArgumentNullException("node"); if (node.GetRoot() == Root) { if (node.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); if (offset > node.ChildNodes.Length) throw new DomException(DomError.IndexSizeError); if (!IsStartAfter(node, offset)) return !IsEndBefore(node, offset); return false; } return false; } public RangePosition CompareBoundaryTo(RangeType how, IRange sourceRange) { if (sourceRange == null) throw new ArgumentNullException("sourceRange"); if (Root != sourceRange.Head.GetRoot()) throw new DomException(DomError.WrongDocument); Boundary boundary = default(Boundary); Boundary boundary2 = default(Boundary); Boundary boundary3; switch (how) { case RangeType.StartToStart: boundary = _start; boundary3 = default(Boundary); boundary3.Node = sourceRange.Head; boundary3.Offset = sourceRange.Start; boundary2 = boundary3; break; case RangeType.StartToEnd: boundary = _end; boundary3 = default(Boundary); boundary3.Node = sourceRange.Head; boundary3.Offset = sourceRange.Start; boundary2 = boundary3; break; case RangeType.EndToEnd: boundary = _start; boundary3 = default(Boundary); boundary3.Node = sourceRange.Tail; boundary3.Offset = sourceRange.End; boundary2 = boundary3; break; case RangeType.EndToStart: boundary = _end; boundary3 = default(Boundary); boundary3.Node = sourceRange.Tail; boundary3.Offset = sourceRange.End; boundary2 = boundary3; break; default: throw new DomException(DomError.NotSupported); } return boundary.CompareTo(boundary2); } public RangePosition CompareTo(INode node, int offset) { if (node == null) throw new ArgumentNullException("node"); if (Root != _start.Node.GetRoot()) throw new DomException(DomError.WrongDocument); if (node.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); if (offset > node.ChildNodes.Length) throw new DomException(DomError.IndexSizeError); if (IsStartAfter(node, offset)) return RangePosition.Before; if (IsEndBefore(node, offset)) return RangePosition.After; return RangePosition.Equal; } public bool Intersects(INode node) { if (node == null) throw new ArgumentNullException("node"); if (Root == node.GetRoot()) { INode parent = node.Parent; if (parent != null) { int num = parent.ChildNodes.Index(node); if (IsEndAfter(parent, num)) return IsStartBefore(parent, num + 1); return false; } return true; } return false; } public override string ToString() { StringBuilder stringBuilder = Pool.NewStringBuilder(); int start = Start; int end = End; IText text = Head as IText; IText text2 = Tail as IText; if (text != null && Head == Tail) return text.Substring(start, end - start); if (text != null) stringBuilder.Append(text.Substring(start, text.Length - start)); foreach (IText item in CommonAncestor.Descendents<IText>()) { if (IsStartBefore(item, 0) && IsEndAfter(item, item.Length)) stringBuilder.Append(item.Text); } if (text2 != null) stringBuilder.Append(text2.Substring(0, end)); return stringBuilder.ToPool(); } private bool IsStartBefore(INode node, int offset) { return _start < new Boundary { Node = node, Offset = offset }; } private bool IsStartAfter(INode node, int offset) { return _start > new Boundary { Node = node, Offset = offset }; } private bool IsEndBefore(INode node, int offset) { return _end < new Boundary { Node = node, Offset = offset }; } private bool IsEndAfter(INode node, int offset) { return _end > new Boundary { Node = node, Offset = offset }; } private bool IsPartiallyContained(INode node) { bool flag = node.IsInclusiveAncestorOf(_start.Node); bool flag2 = node.IsInclusiveAncestorOf(_end.Node); if (!flag || flag2) return !flag & flag2; return true; } } }