bool ReadNode(HtmlElement parentElement, out HtmlNode node, string endElementLocalName = null) { if (MoveNext()) { switch (Current.Type) { case HtmlTagType.Define: node = new HtmlDocumentType(Current.Attributes, parentElement); return(true); case HtmlTagType.Single: node = new HtmlElement(Current.Name, Current.Attributes, parentElement); return(true); case HtmlTagType.Start: { HtmlElement element = new HtmlElement(Current.Name, Current.Attributes, parentElement); node = element; if (HtmlAPI.IsContentTag(element.LocalName)) { TextTokenizer.SkipIgnoreCharacters(); string text = TextTokenizer.ReadTo($"</{element.LocalName}>").TrimEnd(); if (text.Length > 0) { node.ChildNodes.Add(new HtmlText(text, element)); } return(true); } Tags.Add(element.LocalName); HtmlNode lastNode = null; while (ReadNode(element, out HtmlNode childNode, element.LocalName)) { if (childNode is null) { continue; } if (lastNode != null) { lastNode.NextSibling = childNode; childNode.PreviousSibling = lastNode; } node.ChildNodes.Add(childNode); lastNode = childNode; } return(true); } case HtmlTagType.End: if (endElementLocalName != null && !HtmlAPI.Equals(Current.Name, endElementLocalName)) { if (Tags.Contains(Current.Name)) { Buffer.Push(Current); } else { node = null; return(true); } } if (Tags.Count > 0) { Tags.RemoveAt(Tags.Count - 1); } break; case HtmlTagType.Text: node = new HtmlText(HtmlAPI.Unescape(Current.Content), parentElement); return(true); case HtmlTagType.Comment: node = new HtmlComment(Current.Content, parentElement); return(true); default: throw new NotImplementedException(Current.Type.ToString()); } } node = null; return(false); }