/// <summary> /// The push attribute name start. /// </summary> /// <param name="index">The index.</param> private void PushAttributeNameStart(int index) { this.currentattribute = this.CreateAttribute(); this.currentattribute.NameStartIndex = index; this.currentattribute.Line = this.line; this.currentattribute.LinePosition = this.lineposition; this.currentattribute.StreamPosition = index; }
/// <summary> /// The write attribute. /// </summary> /// <param name="outText"> /// The out text. /// </param> /// <param name="att"> /// The att. /// </param> internal void WriteAttribute(TextWriter outText, HtmlAttribute att) { string name; string quote = att.QuoteType == AttributeValueQuote.DoubleQuote ? "\"" : "'"; if (this.OwnerDocument.OptionOutputAsXml) { name = this.OwnerDocument.OptionOutputUpperCase ? att.XmlName.ToUpper() : att.XmlName; if (this.OwnerDocument.OptionOutputOriginalCase) { name = att.OriginalName; } outText.Write(" " + name + "=" + quote + HtmlDocument.HtmlEncode(att.XmlValue) + quote); } else { name = this.OwnerDocument.OptionOutputUpperCase ? att.Name.ToUpper() : att.Name; if (att.Name.Length >= 4) { if ((att.Name[0] == '<') && (att.Name[1] == '%') && (att.Name[att.Name.Length - 1] == '>') && (att.Name[att.Name.Length - 2] == '%')) { outText.Write(" " + name); return; } } if (this.OwnerDocument.OptionOutputOptimizeAttributeValues) { if (att.Value.IndexOfAny(new[] { (char)10, (char)13, (char)9, ' ' }) < 0) { outText.Write(" " + name + "=" + att.Value); } else { outText.Write(" " + name + "=" + quote + att.Value + quote); } } else { outText.Write(" " + name + "=" + quote + att.Value + quote); } } }
/// <summary> /// The parse. /// </summary> private void Parse() { int lastquote = 0; this.LastNodes = new Dictionary<string, HtmlNode>(); this.currentchar = 0; this.fullcomment = false; this.parseerrors = new List<HtmlParseError>(); this.line = 1; this.lineposition = 1; this.maxlineposition = 1; this.state = ParseState.Text; this.oldstate = this.state; this.documentnode.InnerLength = this.Text.Length; this.documentnode.OuterLength = this.Text.Length; this.RemainderOffset = this.Text.Length; this.lastparentnode = this.documentnode; this.currentnode = this.CreateNode(HtmlNodeType.Text, 0); this.currentattribute = null; this.cuurrentindex = 0; this.PushNodeStart(HtmlNodeType.Text, 0); while (this.cuurrentindex < this.Text.Length) { this.currentchar = this.Text[this.cuurrentindex]; this.IncrementPosition(); switch (this.state) { case ParseState.Text: if (this.NewCheck()) { continue; } break; case ParseState.WhichTag: if (this.NewCheck()) { continue; } if (this.currentchar == '/') { this.PushNodeNameStart(false, this.cuurrentindex); } else { this.PushNodeNameStart(true, this.cuurrentindex - 1); this.DecrementPosition(); } this.state = ParseState.Tag; break; case ParseState.Tag: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { this.PushNodeNameEnd(this.cuurrentindex - 1); if (this.state != ParseState.Tag) { continue; } this.state = ParseState.BetweenAttributes; continue; } if (this.currentchar == '/') { this.PushNodeNameEnd(this.cuurrentindex - 1); if (this.state != ParseState.Tag) { continue; } this.state = ParseState.EmptyTag; continue; } if (this.currentchar == '>') { this.PushNodeNameEnd(this.cuurrentindex - 1); if (this.state != ParseState.Tag) { continue; } if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.Tag) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); } break; case ParseState.BetweenAttributes: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { continue; } if ((this.currentchar == '/') || (this.currentchar == '?')) { this.state = ParseState.EmptyTag; continue; } if (this.currentchar == '>') { if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.BetweenAttributes) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } this.PushAttributeNameStart(this.cuurrentindex - 1); this.state = ParseState.AttributeName; break; case ParseState.EmptyTag: if (this.NewCheck()) { continue; } if (this.currentchar == '>') { if (!this.PushNodeEnd(this.cuurrentindex, true)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.EmptyTag) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } this.state = ParseState.BetweenAttributes; break; case ParseState.AttributeName: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { this.PushAttributeNameEnd(this.cuurrentindex - 1); this.state = ParseState.AttributeBeforeEquals; continue; } if (this.currentchar == '=') { this.PushAttributeNameEnd(this.cuurrentindex - 1); this.state = ParseState.AttributeAfterEquals; continue; } if (this.currentchar == '>') { this.PushAttributeNameEnd(this.cuurrentindex - 1); if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.AttributeName) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } break; case ParseState.AttributeBeforeEquals: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { continue; } if (this.currentchar == '>') { if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.AttributeBeforeEquals) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } if (this.currentchar == '=') { this.state = ParseState.AttributeAfterEquals; continue; } // no equals, no whitespace, it's a new attrribute starting this.state = ParseState.BetweenAttributes; this.DecrementPosition(); break; case ParseState.AttributeAfterEquals: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { continue; } if ((this.currentchar == '\'') || (this.currentchar == '"')) { this.state = ParseState.QuotedAttributeValue; this.PushAttributeValueStart(this.cuurrentindex, this.currentchar); lastquote = this.currentchar; continue; } if (this.currentchar == '>') { if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.AttributeAfterEquals) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } this.PushAttributeValueStart(this.cuurrentindex - 1); this.state = ParseState.AttributeValue; break; case ParseState.AttributeValue: if (this.NewCheck()) { continue; } if (IsWhiteSpace(this.currentchar)) { this.PushAttributeValueEnd(this.cuurrentindex - 1); this.state = ParseState.BetweenAttributes; continue; } if (this.currentchar == '>') { this.PushAttributeValueEnd(this.cuurrentindex - 1); if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } if (this.state != ParseState.AttributeValue) { continue; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } break; case ParseState.QuotedAttributeValue: if (this.currentchar == lastquote) { this.PushAttributeValueEnd(this.cuurrentindex - 1); this.state = ParseState.BetweenAttributes; continue; } if (this.currentchar == '<') { if (this.cuurrentindex < this.Text.Length) { if (this.Text[this.cuurrentindex] == '%') { this.oldstate = this.state; this.state = ParseState.ServerSideCode; continue; } } } break; case ParseState.Comment: if (this.currentchar == '>') { if (this.fullcomment) { if ((this.Text[this.cuurrentindex - 2] != '-') || (this.Text[this.cuurrentindex - 3] != '-')) { continue; } } if (!this.PushNodeEnd(this.cuurrentindex, false)) { // stop parsing this.cuurrentindex = this.Text.Length; break; } this.state = ParseState.Text; this.PushNodeStart(HtmlNodeType.Text, this.cuurrentindex); continue; } break; case ParseState.ServerSideCode: if (this.currentchar == '%') { if (this.cuurrentindex < this.Text.Length) { if (this.Text[this.cuurrentindex] == '>') { switch (this.oldstate) { case ParseState.AttributeAfterEquals: this.state = ParseState.AttributeValue; break; case ParseState.BetweenAttributes: this.PushAttributeNameEnd(this.cuurrentindex + 1); this.state = ParseState.BetweenAttributes; break; default: this.state = this.oldstate; break; } this.IncrementPosition(); } } } break; case ParseState.PcData: // look for </tag + 1 char // check buffer end if ((this.currentnode.NameLength + 3) <= (this.Text.Length - (this.cuurrentindex - 1))) { if (string.Compare( this.Text.Substring(this.cuurrentindex - 1, this.currentnode.NameLength + 2), "</" + this.currentnode.NodeName, StringComparison.OrdinalIgnoreCase) == 0) { int c = this.Text[this.cuurrentindex - 1 + 2 + this.currentnode.NodeName.Length]; if ((c == '>') || IsWhiteSpace(c)) { // add the script as a text node HtmlNode script = this.CreateNode( HtmlNodeType.Text, this.currentnode.OuterStartIndex + this.currentnode.OuterLength); script.OuterLength = this.cuurrentindex - 1 - script.OuterStartIndex; this.currentnode.AppendChild(script); this.PushNodeStart(HtmlNodeType.Element, this.cuurrentindex - 1); this.PushNodeNameStart(false, this.cuurrentindex - 1 + 2); this.state = ParseState.Tag; this.IncrementPosition(); } } } break; } } // finish the current work if (this.currentnode.NameStartIndex > 0) { this.PushNodeNameEnd(this.cuurrentindex); } this.PushNodeEnd(this.cuurrentindex, false); // we don't need this anymore this.LastNodes.Clear(); }