AngleSharp by Florian Rappl

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

.NET API 1,172,480 bytes

 ComplexSelector

sealed class ComplexSelector : ISelector
Represents a complex selector. One or more compound selectors separated by combinators.
using AngleSharp.Css; using AngleSharp.Extensions; using AngleSharp.Html; using AngleSharp.Parser.Css; using System; using System.Collections.Generic; using System.Text; namespace AngleSharp.Dom.Css { internal sealed class ComplexSelector : ISelector { private struct CombinatorSelector { public char delimiter; public Func<IElement, IEnumerable<IElement>> transform; public ISelector selector; } private readonly List<CombinatorSelector> selectors; public Priority Specifity { get { Priority priority = default(Priority); for (int i = 0; i < selectors.Count; i++) { priority += selectors[i].selector.Specifity; } return priority; } } public string Text { get { StringBuilder stringBuilder = Pool.NewStringBuilder(); if (selectors.Count > 0) { int num = selectors.Count - 1; for (int i = 0; i < num; i++) { stringBuilder.Append(selectors[i].selector.Text).Append(selectors[i].delimiter); } stringBuilder.Append(selectors[num].selector.Text); } return stringBuilder.ToPool(); } } public int Length => selectors.Count; public bool IsReady { get; set; } public ComplexSelector() { selectors = new List<CombinatorSelector>(); } public bool Match(IElement element) { int num = selectors.Count - 1; if (selectors[num].selector.Match(element)) { if (num > 0) return MatchCascade(num - 1, element); return true; } return false; } public ComplexSelector ConcludeSelector(ISelector selector) { if (!IsReady) { selectors.Add(new CombinatorSelector { selector = selector, transform = null }); IsReady = true; } return this; } public ComplexSelector AppendSelector(ISelector selector, CssCombinator combinator) { if (IsReady) return this; Func<IElement, IEnumerable<IElement>> func = null; char delimiter; switch (combinator) { case CssCombinator.Child: delimiter = '>'; func = ((IElement el) => Single(el.ParentElement)); break; case CssCombinator.AdjacentSibling: delimiter = '+'; func = ((IElement el) => Single(el.PreviousElementSibling)); break; case CssCombinator.Descendent: delimiter = ' '; func = delegate(IElement el) { List<IElement> list2 = new List<IElement>(); for (IElement parentElement2 = el.ParentElement; parentElement2 != null; parentElement2 = parentElement2.ParentElement) { list2.Add(parentElement2); } return list2; }; break; case CssCombinator.Sibling: delimiter = '~'; func = delegate(IElement el) { IElement parentElement = el.ParentElement; if (parentElement == null) return new IElement[0]; List<IElement> list = new List<IElement>(); foreach (INode childNode in parentElement.ChildNodes) { IElement element = childNode as IElement; if (element != null) { if (element == el) return list; list.Add(element); } } return list; }; break; case CssCombinator.Namespace: { string prefix = selector.Text; delimiter = '|'; func = ((IElement el) => Single(el)); selector = new SimpleSelector((IElement el) => MatchesCssNamespace(el, prefix), Priority.Zero, prefix); break; } default: return this; } selectors.Add(new CombinatorSelector { selector = selector, transform = func, delimiter = delimiter }); return this; } public ComplexSelector ClearSelectors() { IsReady = false; selectors.Clear(); return this; } private static bool MatchesCssNamespace(IElement el, string prefix) { if (prefix == "*") return true; string a = el.GetAttribute(Namespaces.XmlNsPrefix) ?? el.NamespaceUri; if (prefix == string.Empty) return a == string.Empty; return a == GetCssNamespace(el, prefix); } private static string GetCssNamespace(IElement el, string prefix) { return el.Owner.StyleSheets.LocateNamespace(prefix) ?? el.LocateNamespace(prefix); } private bool MatchCascade(int pos, IElement element) { foreach (IElement item in selectors[pos].transform(element)) { if (selectors[pos].selector.Match(item) && (pos == 0 || MatchCascade(pos - 1, item))) return true; } return false; } private static IEnumerable<IElement> Single(IElement element) { if (element != null) yield return element; } } }