/// <summary> /// Retrieves unquoted attribute value /// </summary> /// <returns>Attribute value token</returns> public IHtmlAttributeValueToken GetUnquotedAttributeValue(int tagEnd) { // Need to handle <a b=c=d /> and figure out that c=d is a valid pair while // b= is missing a value. One way is to check if 'c' happens to be // one of the known HTML attributes, but it only works for plain HTML // and won't work for ASP.NET controls that can define any attributes // via .NET properties. // Options (value is unquoted) // a. Attribute value is either a sequence of characters or a number // except if it is ID or CLASS. // b. Attribute name is typically a sequence of characters and does not include digits // c. Attribute value is not normally followed by = Debug.Assert(!_cs.IsAtString()); AttributeValueToken token = null; int start = _cs.Position; int end = _cs.Position; ITextRange nextTokenRange; NextTokenType nextTokenType = NextToken.PeekNextToken(_cs, tagEnd, out nextTokenRange); switch (nextTokenType) { case NextTokenType.None: // attrName={EOF} case NextTokenType.Tag: // attName=<foo case NextTokenType.Equals: // attrName== return(null); case NextTokenType.Number: // attrName = 1.0 case NextTokenType.Unknown: // id=# case NextTokenType.Identifier: // attrName = foo12. There are no know attributes of this form. end = nextTokenRange.End; break; case NextTokenType.Letters: // Legal value. Need to check against attribute names to be sure. bool isKnownAttribute = AttributeTable.IsKnownAttribute(_cs.GetSubstringAt(nextTokenRange.Start, nextTokenRange.Length)); if (!isKnownAttribute) { end = nextTokenRange.End; } break; } char closeQuote = '\0'; if (end > start) { closeQuote = _cs[end - 1]; if (closeQuote != '\'' && closeQuote != '\"') { closeQuote = '\0'; } } token = AttributeValueToken.Create(HtmlToken.FromBounds(start, end), '\0', closeQuote); _cs.Position = end; return(token); }
public static NextTokenType PeekNextToken(HtmlCharStream cs, int tagEnd, out ITextRange range) { NextTokenType tokenType = NextTokenType.Unknown; int current = cs.Position; if (cs.IsEndOfStream() || cs.Position == tagEnd) { range = new TextRange(); return(NextTokenType.None); } int start = cs.Position; while (cs.IsWhiteSpace()) { cs.MoveToNextChar(); } if (cs.IsEndOfStream() || cs.Position == tagEnd) { range = TextRange.FromBounds(start, cs.Position); return(NextTokenType.Unknown); } if (cs.IsAtTagDelimiter()) { tokenType = NextTokenType.Tag; } else if (cs.CurrentChar == '=') { tokenType = NextTokenType.Equals; } else { int digits = 0; bool firstLetter = false; int length = 0; int chars = 0; if (cs.IsAnsiLetter()) { firstLetter = true; } while (!cs.IsEndOfStream() && !cs.IsWhiteSpace() && !cs.IsAtTagDelimiter() && cs.CurrentChar != '=' && cs.Position < tagEnd) { if (cs.IsAnsiLetter() || cs.CurrentChar == '_' || cs.CurrentChar == '-') { chars++; } else if (cs.IsDecimal() || cs.CurrentChar == '.') { digits++; } cs.MoveToNextChar(); length++; } if (length > 0) { if (length == digits) { tokenType = NextTokenType.Number; } else if (length == chars) { tokenType = NextTokenType.Letters; } else if (firstLetter) { tokenType = NextTokenType.Identifier; } } } range = TextRange.FromBounds(start, cs.Position); cs.Position = current; return(tokenType); }