AngleSharp by Florian Rappl

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

 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) { _start = new Boundary { Offset = 0, Node = document }; _end = new Boundary { Offset = 0, Node = document }; } 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); _start = new Boundary { Node = parent, Offset = num }; _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; _start = new Boundary { Node = refNode, Offset = 0 }; _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 characterData = (ICharacterData)start.Node; int count = end.Offset - start.Offset; characterData.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 characterData2 = (ICharacterData)start.Node; int count2 = end.Offset - start.Offset; characterData2.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 characterData3 = (ICharacterData)end.Node; int offset4 = end.Offset; characterData3.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); if (!start.Node.IsInclusiveAncestorOf(end.Node)) { INode node4 = start.Node; while (node4.Parent != null && !node4.IsInclusiveAncestorOf(end.Node)) { node4 = node4.Parent; } Boundary 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); Range range = new Range(start, new Boundary { Node = node2, Offset = node2.ChildNodes.Length }); IDocumentFragment child2 = range.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); Boundary start2 = default(Boundary); start2.Node = node3; start2.Offset = 0; Range range2 = new Range(start2, end); IDocumentFragment child4 = range2.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); 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); Range range = new Range(start, new Boundary { Node = node2, Offset = node2.ChildNodes.Length }); IDocumentFragment child2 = range.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 start2 = default(Boundary); start2.Node = node3; start2.Offset = 0; Range range2 = new Range(start2, end); IDocumentFragment child4 = range2.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; if (node.Parent != null) 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) return false; if (node.NodeType == NodeType.DocumentType) throw new DomException(DomError.InvalidNodeType); if (offset > node.ChildNodes.Length) throw new DomException(DomError.IndexSizeError); if (_start > new Boundary { Node = node, Offset = offset } || _end < new Boundary { Node = node, Offset = offset }) return false; return true; } 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; Boundary other; switch (how) { case RangeType.StartToStart: { boundary = _start; Boundary boundary5 = default(Boundary); boundary5.Node = sourceRange.Head; boundary5.Offset = sourceRange.Start; other = boundary5; break; } case RangeType.StartToEnd: { boundary = _end; Boundary boundary4 = default(Boundary); boundary4.Node = sourceRange.Head; boundary4.Offset = sourceRange.Start; other = boundary4; break; } case RangeType.EndToEnd: { boundary = _start; Boundary boundary3 = default(Boundary); boundary3.Node = sourceRange.Tail; boundary3.Offset = sourceRange.End; other = boundary3; break; } case RangeType.EndToStart: { boundary = _end; Boundary boundary2 = default(Boundary); boundary2.Node = sourceRange.Tail; boundary2.Offset = sourceRange.End; other = boundary2; break; } default: throw new DomException(DomError.NotSupported); } return boundary.CompareTo(other); } 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 (_start > new Boundary { Node = node, Offset = offset }) return RangePosition.Before; if (_end < new Boundary { Node = node, Offset = offset }) return RangePosition.After; return RangePosition.Equal; } public bool Intersects(INode node) { if (node == null) throw new ArgumentNullException("node"); if (Root != node.GetRoot()) return false; INode parent = node.Parent; if (parent == null) return true; int num = parent.ChildNodes.Index(node); if (_end > new Boundary { Node = parent, Offset = num }) return _start < new Boundary { Node = parent, Offset = num + 1 }; 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)); IEnumerable<IText> enumerable = CommonAncestor.Descendents<IText>(); foreach (IText item in enumerable) { if (_start < new Boundary { Node = item, Offset = 0 } && _end > new Boundary { Node = item, Offset = item.Length }) stringBuilder.Append(item.Text); } if (text2 != null) stringBuilder.Append(text2.Substring(0, end)); return stringBuilder.ToPool(); } private bool IsPartiallyContained(INode node) { bool flag = node.IsInclusiveAncestorOf(_start.Node); bool flag2 = node.IsInclusiveAncestorOf(_end.Node); if (!flag || flag2) { if (!flag) return flag2; return false; } return true; } } }