/// <summary> /// Gets a HTML attribute type /// </summary> /// <param name="tagNameInLowercase">Tag name in lowercase</param> /// <param name="tagFlags">Tag flags</param> /// <param name="attributeNameInLowercase">Attribute name in lowercase</param> /// <param name="attributes">List of attributes</param> /// <returns>Attribute type</returns> public HtmlAttributeType GetAttributeType(string tagNameInLowercase, HtmlTagFlags tagFlags, string attributeNameInLowercase, List <HtmlAttribute> attributes) { HtmlAttributeType attributeType = HtmlAttributeType.Unknown; if (attributeNameInLowercase == "class") { attributeType = HtmlAttributeType.ClassName; } else if (attributeNameInLowercase == "style") { attributeType = HtmlAttributeType.Style; } else if (IsEventAttribute(attributeNameInLowercase)) { attributeType = HtmlAttributeType.Event; } if (attributeType == HtmlAttributeType.Unknown && !tagFlags.IsSet(HtmlTagFlags.Xml)) { if (IsBooleanAttribute(attributeNameInLowercase)) { attributeType = HtmlAttributeType.Boolean; } else if (IsNumericAttribute(tagNameInLowercase, attributeNameInLowercase)) { attributeType = HtmlAttributeType.Numeric; } else if (IsUriBasedAttribute(tagNameInLowercase, attributeNameInLowercase, attributes)) { attributeType = HtmlAttributeType.Uri; } } if (attributeType == HtmlAttributeType.Unknown) { attributeType = IsXmlBasedAttribute(attributeNameInLowercase) ? HtmlAttributeType.Xml : HtmlAttributeType.Text; } return(attributeType); }
/// <summary> /// Parses a end tag /// </summary> /// <param name="tagName">Tag name</param> /// <param name="tagNameInLowercase">Tag name in lowercase</param> private void ParseEndTag(string tagName, string tagNameInLowercase) { int endTagIndex = 0; int lastTagIndex = _tagStack.Count - 1; bool tagNameNotEmpty = !string.IsNullOrEmpty(tagName); HtmlParsingHandlers.EndTagDelegate endTagHandler = _handlers.EndTag; if (tagNameNotEmpty) { for (endTagIndex = lastTagIndex; endTagIndex >= 0; endTagIndex--) { if (_tagStack[endTagIndex].NameInLowercase == tagNameInLowercase) { break; } } } if (endTagIndex >= 0) { // Close all the open elements, up the stack if (endTagHandler != null) { for (int tagIndex = lastTagIndex; tagIndex >= endTagIndex; tagIndex--) { HtmlTag startTag = _tagStack[tagIndex]; string startTagNameInLowercase = startTag.NameInLowercase; HtmlTagFlags startTagFlags = startTag.Flags; string endTagName; if (tagNameNotEmpty && tagNameInLowercase == startTagNameInLowercase) { endTagName = tagName; } else { endTagName = startTag.Name; } if (_xmlTagStack.Count > 0 && !startTagFlags.IsSet(HtmlTagFlags.NonIndependent)) { _xmlTagStack.Pop(); } var endTag = new HtmlTag(endTagName, startTagNameInLowercase, startTagFlags); endTagHandler(_context, endTag); } } // Remove the open elements from the stack if (endTagIndex <= lastTagIndex) { int tagToRemoveStartIndex = endTagIndex; int tagsToRemoveCount = lastTagIndex - endTagIndex + 1; _tagStack.RemoveRange(tagToRemoveStartIndex, tagsToRemoveCount); } } else if (tagNameNotEmpty && _conditionalCommentOpened) { if (_xmlTagStack.Count > 0 && _tagTypeDeterminer.IsXmlBasedTag(tagNameInLowercase)) { _xmlTagStack.Pop(); } var endTag = new HtmlTag(tagName, tagNameInLowercase, GetTagFlagsByName(tagNameInLowercase)); endTagHandler?.Invoke(_context, endTag); } }
/// <summary> /// Parses a start tag /// </summary> /// <param name="tagName">Tag name</param> /// <param name="tagNameInLowercase">Tag name in lowercase</param> /// <param name="attributes">List of attributes</param> /// <param name="isEmptyTag">Flag that tag is empty</param> private void ParseStartTag(string tagName, string tagNameInLowercase, List <HtmlAttribute> attributes, bool isEmptyTag) { HtmlTagFlags tagFlags = GetTagFlagsByName(tagNameInLowercase); if (tagFlags.IsSet(HtmlTagFlags.Optional)) { HtmlTag lastStackedTag = _tagStack.LastOrDefault(); if (lastStackedTag != null && lastStackedTag.NameInLowercase == tagNameInLowercase) { ParseEndTag(lastStackedTag.Name, lastStackedTag.NameInLowercase); } else { if (tagNameInLowercase == "body" && _tagStack.Any(t => t.NameInLowercase == "head")) { HtmlTag headTag = _tagStack.First(t => t.NameInLowercase == "head"); ParseEndTag(headTag.Name, headTag.NameInLowercase); } } } if (tagFlags.IsSet(HtmlTagFlags.Empty)) { isEmptyTag = true; } else if (isEmptyTag) { tagFlags |= HtmlTagFlags.Empty; } int attributeCount = attributes.Count; for (int attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { HtmlAttribute attribute = attributes[attributeIndex]; attribute.Type = _attributeTypeDeterminer.GetAttributeType(tagNameInLowercase, tagFlags, attribute.NameInLowercase, attributes); } var tag = new HtmlTag(tagName, tagNameInLowercase, attributes, tagFlags); if (!isEmptyTag) { if (_conditionalCommentOpened) { HtmlConditionalComment lastConditionalComment = _conditionalCommentStack.Peek(); HtmlConditionalCommentType lastConditionalCommentType = lastConditionalComment.Type; if (tagFlags.IsSet(HtmlTagFlags.EmbeddedCode) || lastConditionalCommentType == HtmlConditionalCommentType.RevealedValidating || lastConditionalCommentType == HtmlConditionalCommentType.RevealedValidatingSimplified) { _tagStack.Add(tag); } } else { _tagStack.Add(tag); } } _handlers.StartTag?.Invoke(_context, tag); if (tagFlags.IsSet(HtmlTagFlags.Xml) && !tagFlags.IsSet(HtmlTagFlags.NonIndependent)) { _xmlTagStack.Push(tagNameInLowercase); } }