| Index: pkg/third_party/html5lib/lib/src/tokenizer.dart
|
| diff --git a/pkg/third_party/html5lib/lib/src/tokenizer.dart b/pkg/third_party/html5lib/lib/src/tokenizer.dart
|
| deleted file mode 100644
|
| index 1b6311497ac4a3b4c188032a6d852a276c13804c..0000000000000000000000000000000000000000
|
| --- a/pkg/third_party/html5lib/lib/src/tokenizer.dart
|
| +++ /dev/null
|
| @@ -1,1886 +0,0 @@
|
| -library tokenizer;
|
| -
|
| -import 'dart:collection';
|
| -import 'package:html5lib/parser.dart' show HtmlParser;
|
| -import 'constants.dart';
|
| -import 'inputstream.dart';
|
| -import 'token.dart';
|
| -import 'utils.dart';
|
| -
|
| -// Group entities by their first character, for faster lookups
|
| -
|
| -// TODO(jmesserly): we could use a better data structure here like a trie, if
|
| -// we had it implemented in Dart.
|
| -Map<String, List<String>> entitiesByFirstChar = (() {
|
| - var result = {};
|
| - for (var k in entities.keys) {
|
| - result.putIfAbsent(k[0], () => []).add(k);
|
| - }
|
| - return result;
|
| -})();
|
| -
|
| -// TODO(jmesserly): lots of ways to make this faster:
|
| -// - use char codes everywhere instead of 1-char strings
|
| -// - use switch instead of contains, indexOf
|
| -// - use switch instead of the sequential if tests
|
| -// - avoid string concat
|
| -
|
| -/// This class takes care of tokenizing HTML.
|
| -class HtmlTokenizer implements Iterator<Token> {
|
| - // TODO(jmesserly): a lot of these could be made private
|
| -
|
| - final HtmlInputStream stream;
|
| -
|
| - final bool lowercaseElementName;
|
| -
|
| - final bool lowercaseAttrName;
|
| -
|
| - /// True to generate spans in for [Token.span].
|
| - final bool generateSpans;
|
| -
|
| - /// True to generate spans for attributes.
|
| - final bool attributeSpans;
|
| -
|
| - /// This reference to the parser is used for correct CDATA handling.
|
| - /// The [HtmlParser] will set this at construction time.
|
| - HtmlParser parser;
|
| -
|
| - final Queue<Token> tokenQueue;
|
| -
|
| - /// Holds the token that is currently being processed.
|
| - Token currentToken;
|
| -
|
| - /// Holds a reference to the method to be invoked for the next parser state.
|
| - // TODO(jmesserly): the type should be "Predicate" but a dart2js checked mode
|
| - // bug prevents us from doing that. See http://dartbug.com/12465
|
| - Function state;
|
| -
|
| - String temporaryBuffer;
|
| -
|
| - int _lastOffset;
|
| -
|
| - // TODO(jmesserly): ideally this would be a LinkedHashMap and we wouldn't add
|
| - // an item until it's ready. But the code doesn't have a clear notion of when
|
| - // it's "done" with the attribute.
|
| - List<TagAttribute> _attributes;
|
| - Set<String> _attributeNames;
|
| -
|
| - HtmlTokenizer(doc, {String encoding, bool parseMeta: true,
|
| - this.lowercaseElementName: true, this.lowercaseAttrName: true,
|
| - bool generateSpans: false, String sourceUrl, this.attributeSpans: false})
|
| - : stream = new HtmlInputStream(
|
| - doc, encoding, parseMeta, generateSpans, sourceUrl),
|
| - tokenQueue = new Queue(),
|
| - generateSpans = generateSpans {
|
| - reset();
|
| - }
|
| -
|
| - TagToken get currentTagToken => currentToken;
|
| - DoctypeToken get currentDoctypeToken => currentToken;
|
| - StringToken get currentStringToken => currentToken;
|
| -
|
| - Token _current;
|
| - Token get current => _current;
|
| -
|
| - String get _attributeName => _attributes.last.name;
|
| - set _attributeName(String value) {
|
| - _attributes.last.name = value;
|
| - }
|
| -
|
| - String get _attributeValue => _attributes.last.value;
|
| - set _attributeValue(String value) {
|
| - _attributes.last.value = value;
|
| - }
|
| -
|
| - void _markAttributeEnd(int offset) {
|
| - if (attributeSpans) _attributes.last.end = stream.position + offset;
|
| - }
|
| -
|
| - void _markAttributeValueStart(int offset) {
|
| - if (attributeSpans) _attributes.last.startValue = stream.position + offset;
|
| - }
|
| -
|
| - void _markAttributeValueEnd(int offset) {
|
| - if (attributeSpans) {
|
| - _attributes.last.endValue = stream.position + offset;
|
| - _markAttributeEnd(offset);
|
| - }
|
| - }
|
| -
|
| - // Note: we could track the name span here, if we need it.
|
| - void _markAttributeNameEnd(int offset) => _markAttributeEnd(offset);
|
| -
|
| - void _addAttribute(String name) {
|
| - if (_attributes == null) _attributes = [];
|
| - var attr = new TagAttribute(name);
|
| - _attributes.add(attr);
|
| - if (attributeSpans) attr.start = stream.position - name.length;
|
| - }
|
| -
|
| - /// This is where the magic happens.
|
| - ///
|
| - /// We do our usually processing through the states and when we have a token
|
| - /// to return we yield the token which pauses processing until the next token
|
| - /// is requested.
|
| - bool moveNext() {
|
| - // Start processing. When EOF is reached state will return false;
|
| - // instead of true and the loop will terminate.
|
| - while (stream.errors.length == 0 && tokenQueue.length == 0) {
|
| - if (!state()) {
|
| - _current = null;
|
| - return false;
|
| - }
|
| - }
|
| - if (stream.errors.length > 0) {
|
| - _current = new ParseErrorToken(stream.errors.removeFirst());
|
| - } else {
|
| - assert (tokenQueue.length > 0);
|
| - _current = tokenQueue.removeFirst();
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /// Resets the tokenizer state. Calling this does not reset the [stream] or
|
| - /// the [parser].
|
| - void reset() {
|
| - _lastOffset = 0;
|
| - tokenQueue.clear();
|
| - currentToken = null;
|
| - temporaryBuffer = null;
|
| - _attributes = null;
|
| - _attributeNames = null;
|
| - state = dataState;
|
| - }
|
| -
|
| - /// Adds a token to the queue. Sets the span if needed.
|
| - void _addToken(Token token) {
|
| - if (generateSpans && token.span == null) {
|
| - int offset = stream.position;
|
| - token.span = stream.fileInfo.span(_lastOffset, offset);
|
| - if (token is! ParseErrorToken) {
|
| - _lastOffset = offset;
|
| - }
|
| - }
|
| - tokenQueue.add(token);
|
| - }
|
| -
|
| - /// This function returns either U+FFFD or the character based on the
|
| - /// decimal or hexadecimal representation. It also discards ";" if present.
|
| - /// If not present it will add a [ParseErrorToken].
|
| - String consumeNumberEntity(bool isHex) {
|
| - var allowed = isDigit;
|
| - var radix = 10;
|
| - if (isHex) {
|
| - allowed = isHexDigit;
|
| - radix = 16;
|
| - }
|
| -
|
| - var charStack = [];
|
| -
|
| - // Consume all the characters that are in range while making sure we
|
| - // don't hit an EOF.
|
| - var c = stream.char();
|
| - while (allowed(c) && c != EOF) {
|
| - charStack.add(c);
|
| - c = stream.char();
|
| - }
|
| -
|
| - // Convert the set of characters consumed to an int.
|
| - var charAsInt = parseIntRadix(charStack.join(), radix);
|
| -
|
| - // Certain characters get replaced with others
|
| - var char = replacementCharacters[charAsInt];
|
| - if (char != null) {
|
| - _addToken(new ParseErrorToken(
|
| - "illegal-codepoint-for-numeric-entity",
|
| - messageParams: {"charAsInt": charAsInt}));
|
| - } else if ((0xD800 <= charAsInt && charAsInt <= 0xDFFF)
|
| - || (charAsInt > 0x10FFFF)) {
|
| - char = "\uFFFD";
|
| - _addToken(new ParseErrorToken(
|
| - "illegal-codepoint-for-numeric-entity",
|
| - messageParams: {"charAsInt": charAsInt}));
|
| - } else {
|
| - // Should speed up this check somehow (e.g. move the set to a constant)
|
| - if ((0x0001 <= charAsInt && charAsInt <= 0x0008) ||
|
| - (0x000E <= charAsInt && charAsInt <= 0x001F) ||
|
| - (0x007F <= charAsInt && charAsInt <= 0x009F) ||
|
| - (0xFDD0 <= charAsInt && charAsInt <= 0xFDEF) ||
|
| - const [0x000B, 0xFFFE, 0xFFFF, 0x1FFFE,
|
| - 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE,
|
| - 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE,
|
| - 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
|
| - 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE,
|
| - 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE,
|
| - 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE,
|
| - 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE,
|
| - 0xFFFFF, 0x10FFFE, 0x10FFFF].contains(charAsInt)) {
|
| - _addToken(new ParseErrorToken(
|
| - "illegal-codepoint-for-numeric-entity",
|
| - messageParams: {"charAsInt": charAsInt}));
|
| - }
|
| - char = new String.fromCharCodes([charAsInt]);
|
| - }
|
| -
|
| - // Discard the ; if present. Otherwise, put it back on the queue and
|
| - // invoke parseError on parser.
|
| - if (c != ";") {
|
| - _addToken(new ParseErrorToken(
|
| - "numeric-entity-without-semicolon"));
|
| - stream.unget(c);
|
| - }
|
| - return char;
|
| - }
|
| -
|
| - void consumeEntity({String allowedChar, bool fromAttribute: false}) {
|
| - // Initialise to the default output for when no entity is matched
|
| - var output = "&";
|
| -
|
| - var charStack = [stream.char()];
|
| - if (isWhitespace(charStack[0]) || charStack[0] == '<' || charStack[0] == '&'
|
| - || charStack[0] == EOF || allowedChar == charStack[0]) {
|
| - stream.unget(charStack[0]);
|
| - } else if (charStack[0] == "#") {
|
| - // Read the next character to see if it's hex or decimal
|
| - bool hex = false;
|
| - charStack.add(stream.char());
|
| - if (charStack.last == 'x' || charStack.last == 'X') {
|
| - hex = true;
|
| - charStack.add(stream.char());
|
| - }
|
| -
|
| - // charStack.last should be the first digit
|
| - if (hex && isHexDigit(charStack.last) ||
|
| - (!hex && isDigit(charStack.last))) {
|
| - // At least one digit found, so consume the whole number
|
| - stream.unget(charStack.last);
|
| - output = consumeNumberEntity(hex);
|
| - } else {
|
| - // No digits found
|
| - _addToken(new ParseErrorToken("expected-numeric-entity"));
|
| - stream.unget(charStack.removeLast());
|
| - output = "&${charStack.join()}";
|
| - }
|
| - } else {
|
| - // At this point in the process might have named entity. Entities
|
| - // are stored in the global variable "entities".
|
| - //
|
| - // Consume characters and compare to these to a substring of the
|
| - // entity names in the list until the substring no longer matches.
|
| - var filteredEntityList = entitiesByFirstChar[charStack[0]];
|
| - if (filteredEntityList == null) filteredEntityList = const [];
|
| -
|
| - while (charStack.last != EOF) {
|
| - var name = charStack.join();
|
| - filteredEntityList = filteredEntityList.where(
|
| - (e) => e.startsWith(name)).toList();
|
| -
|
| - if (filteredEntityList.length == 0) {
|
| - break;
|
| - }
|
| - charStack.add(stream.char());
|
| - }
|
| -
|
| - // At this point we have a string that starts with some characters
|
| - // that may match an entity
|
| - String entityName = null;
|
| -
|
| - // Try to find the longest entity the string will match to take care
|
| - // of ¬i for instance.
|
| -
|
| - int entityLen;
|
| - for (entityLen = charStack.length - 1; entityLen > 1; entityLen--) {
|
| - var possibleEntityName = charStack.sublist(0, entityLen).join();
|
| - if (entities.containsKey(possibleEntityName)) {
|
| - entityName = possibleEntityName;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (entityName != null) {
|
| - var lastChar = entityName[entityName.length - 1];
|
| - if (lastChar != ";") {
|
| - _addToken(new ParseErrorToken(
|
| - "named-entity-without-semicolon"));
|
| - }
|
| - if (lastChar != ";" && fromAttribute &&
|
| - (isLetterOrDigit(charStack[entityLen]) ||
|
| - charStack[entityLen] == '=')) {
|
| - stream.unget(charStack.removeLast());
|
| - output = "&${charStack.join()}";
|
| - } else {
|
| - output = entities[entityName];
|
| - stream.unget(charStack.removeLast());
|
| - output = '${output}${slice(charStack, entityLen).join()}';
|
| - }
|
| - } else {
|
| - _addToken(new ParseErrorToken("expected-named-entity"));
|
| - stream.unget(charStack.removeLast());
|
| - output = "&${charStack.join()}";
|
| - }
|
| - }
|
| - if (fromAttribute) {
|
| - _attributeValue = '$_attributeValue$output';
|
| - } else {
|
| - var token;
|
| - if (isWhitespace(output)) {
|
| - token = new SpaceCharactersToken(output);
|
| - } else {
|
| - token = new CharactersToken(output);
|
| - }
|
| - _addToken(token);
|
| - }
|
| - }
|
| -
|
| - /// This method replaces the need for "entityInAttributeValueState".
|
| - void processEntityInAttribute(String allowedChar) {
|
| - consumeEntity(allowedChar: allowedChar, fromAttribute: true);
|
| - }
|
| -
|
| - /// This method is a generic handler for emitting the tags. It also sets
|
| - /// the state to "data" because that's what's needed after a token has been
|
| - /// emitted.
|
| - void emitCurrentToken() {
|
| - var token = currentToken;
|
| - // Add token to the queue to be yielded
|
| - if (token is TagToken) {
|
| - if (lowercaseElementName) {
|
| - token.name = asciiUpper2Lower(token.name);
|
| - }
|
| - if (token is EndTagToken) {
|
| - if (_attributes != null) {
|
| - _addToken(new ParseErrorToken("attributes-in-end-tag"));
|
| - }
|
| - if (token.selfClosing) {
|
| - _addToken(new ParseErrorToken("this-closing-flag-on-end-tag"));
|
| - }
|
| - } else if (token is StartTagToken) {
|
| - // HTML5 specific normalizations to the token stream.
|
| - // Convert the list into a map where first key wins.
|
| - token.data = new LinkedHashMap<Object, String>();
|
| - if (_attributes != null) {
|
| - for (var attr in _attributes) {
|
| - token.data.putIfAbsent(attr.name, () => attr.value);
|
| - }
|
| - if (attributeSpans) token.attributeSpans = _attributes;
|
| - }
|
| - }
|
| - _attributes = null;
|
| - _attributeNames = null;
|
| - }
|
| - _addToken(token);
|
| - state = dataState;
|
| - }
|
| -
|
| - // Below are the various tokenizer states worked out.
|
| -
|
| - bool dataState() {
|
| - var data = stream.char();
|
| - if (data == "&") {
|
| - state = entityDataState;
|
| - } else if (data == "<") {
|
| - state = tagOpenState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\u0000"));
|
| - } else if (data == EOF) {
|
| - // Tokenization ends.
|
| - return false;
|
| - } else if (isWhitespace(data)) {
|
| - // Directly after emitting a token you switch back to the "data
|
| - // state". At that point spaceCharacters are important so they are
|
| - // emitted separately.
|
| - _addToken(new SpaceCharactersToken(
|
| - '${data}${stream.charsUntil(spaceCharacters, true)}'));
|
| - // No need to update lastFourChars here, since the first space will
|
| - // have already been appended to lastFourChars and will have broken
|
| - // any <!-- or --> sequences
|
| - } else {
|
| - var chars = stream.charsUntil("&<\u0000");
|
| - _addToken(new CharactersToken('${data}${chars}'));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool entityDataState() {
|
| - consumeEntity();
|
| - state = dataState;
|
| - return true;
|
| - }
|
| -
|
| - bool rcdataState() {
|
| - var data = stream.char();
|
| - if (data == "&") {
|
| - state = characterReferenceInRcdata;
|
| - } else if (data == "<") {
|
| - state = rcdataLessThanSignState;
|
| - } else if (data == EOF) {
|
| - // Tokenization ends.
|
| - return false;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else if (isWhitespace(data)) {
|
| - // Directly after emitting a token you switch back to the "data
|
| - // state". At that point spaceCharacters are important so they are
|
| - // emitted separately.
|
| - _addToken(new SpaceCharactersToken(
|
| - '${data}${stream.charsUntil(spaceCharacters, true)}'));
|
| - } else {
|
| - var chars = stream.charsUntil("&<");
|
| - _addToken(new CharactersToken('${data}${chars}'));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool characterReferenceInRcdata() {
|
| - consumeEntity();
|
| - state = rcdataState;
|
| - return true;
|
| - }
|
| -
|
| - bool rawtextState() {
|
| - var data = stream.char();
|
| - if (data == "<") {
|
| - state = rawtextLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else if (data == EOF) {
|
| - // Tokenization ends.
|
| - return false;
|
| - } else {
|
| - var chars = stream.charsUntil("<\u0000");
|
| - _addToken(new CharactersToken("${data}${chars}"));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataState() {
|
| - var data = stream.char();
|
| - if (data == "<") {
|
| - state = scriptDataLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else if (data == EOF) {
|
| - // Tokenization ends.
|
| - return false;
|
| - } else {
|
| - var chars = stream.charsUntil("<\u0000");
|
| - _addToken(new CharactersToken("${data}${chars}"));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool plaintextState() {
|
| - var data = stream.char();
|
| - if (data == EOF) {
|
| - // Tokenization ends.
|
| - return false;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else {
|
| - _addToken(new CharactersToken(
|
| - '${data}${stream.charsUntil("\u0000")}'));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool tagOpenState() {
|
| - var data = stream.char();
|
| - if (data == "!") {
|
| - state = markupDeclarationOpenState;
|
| - } else if (data == "/") {
|
| - state = closeTagOpenState;
|
| - } else if (isLetter(data)) {
|
| - currentToken = new StartTagToken(data);
|
| - state = tagNameState;
|
| - } else if (data == ">") {
|
| - // XXX In theory it could be something besides a tag name. But
|
| - // do we really care?
|
| - _addToken(new ParseErrorToken(
|
| - "expected-tag-name-but-got-right-bracket"));
|
| - _addToken(new CharactersToken("<>"));
|
| - state = dataState;
|
| - } else if (data == "?") {
|
| - // XXX In theory it could be something besides a tag name. But
|
| - // do we really care?
|
| - _addToken(new ParseErrorToken(
|
| - "expected-tag-name-but-got-question-mark"));
|
| - stream.unget(data);
|
| - state = bogusCommentState;
|
| - } else {
|
| - // XXX
|
| - _addToken(new ParseErrorToken("expected-tag-name"));
|
| - _addToken(new CharactersToken("<"));
|
| - stream.unget(data);
|
| - state = dataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool closeTagOpenState() {
|
| - var data = stream.char();
|
| - if (isLetter(data)) {
|
| - currentToken = new EndTagToken(data);
|
| - state = tagNameState;
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-closing-tag-but-got-right-bracket"));
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-closing-tag-but-got-eof"));
|
| - _addToken(new CharactersToken("</"));
|
| - state = dataState;
|
| - } else {
|
| - // XXX data can be _'_...
|
| - _addToken(new ParseErrorToken(
|
| - "expected-closing-tag-but-got-char", messageParams: {"data": data}));
|
| - stream.unget(data);
|
| - state = bogusCommentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool tagNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = beforeAttributeNameState;
|
| - } else if (data == ">") {
|
| - emitCurrentToken();
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-tag-name"));
|
| - state = dataState;
|
| - } else if (data == "/") {
|
| - state = selfClosingStartTagState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentTagToken.name = '${currentTagToken.name}\uFFFD';
|
| - } else {
|
| - currentTagToken.name = '${currentTagToken.name}$data';
|
| - // (Don't use charsUntil here, because tag names are
|
| - // very short and it's faster to not do anything fancy)
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool rcdataLessThanSignState() {
|
| - var data = stream.char();
|
| - if (data == "/") {
|
| - temporaryBuffer = "";
|
| - state = rcdataEndTagOpenState;
|
| - } else {
|
| - _addToken(new CharactersToken("<"));
|
| - stream.unget(data);
|
| - state = rcdataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool rcdataEndTagOpenState() {
|
| - var data = stream.char();
|
| - if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - state = rcdataEndTagNameState;
|
| - } else {
|
| - _addToken(new CharactersToken("</"));
|
| - stream.unget(data);
|
| - state = rcdataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool _tokenIsAppropriate() {
|
| - return currentToken is TagToken &&
|
| - currentTagToken.name.toLowerCase() == temporaryBuffer.toLowerCase();
|
| - }
|
| -
|
| - bool rcdataEndTagNameState() {
|
| - var appropriate = _tokenIsAppropriate();
|
| - var data = stream.char();
|
| - if (isWhitespace(data) && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = beforeAttributeNameState;
|
| - } else if (data == "/" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = selfClosingStartTagState;
|
| - } else if (data == ">" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - emitCurrentToken();
|
| - state = dataState;
|
| - } else if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - _addToken(new CharactersToken("</$temporaryBuffer"));
|
| - stream.unget(data);
|
| - state = rcdataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool rawtextLessThanSignState() {
|
| - var data = stream.char();
|
| - if (data == "/") {
|
| - temporaryBuffer = "";
|
| - state = rawtextEndTagOpenState;
|
| - } else {
|
| - _addToken(new CharactersToken("<"));
|
| - stream.unget(data);
|
| - state = rawtextState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool rawtextEndTagOpenState() {
|
| - var data = stream.char();
|
| - if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - state = rawtextEndTagNameState;
|
| - } else {
|
| - _addToken(new CharactersToken("</"));
|
| - stream.unget(data);
|
| - state = rawtextState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool rawtextEndTagNameState() {
|
| - var appropriate = _tokenIsAppropriate();
|
| - var data = stream.char();
|
| - if (isWhitespace(data) && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = beforeAttributeNameState;
|
| - } else if (data == "/" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = selfClosingStartTagState;
|
| - } else if (data == ">" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - emitCurrentToken();
|
| - state = dataState;
|
| - } else if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - _addToken(new CharactersToken("</$temporaryBuffer"));
|
| - stream.unget(data);
|
| - state = rawtextState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataLessThanSignState() {
|
| - var data = stream.char();
|
| - if (data == "/") {
|
| - temporaryBuffer = "";
|
| - state = scriptDataEndTagOpenState;
|
| - } else if (data == "!") {
|
| - _addToken(new CharactersToken("<!"));
|
| - state = scriptDataEscapeStartState;
|
| - } else {
|
| - _addToken(new CharactersToken("<"));
|
| - stream.unget(data);
|
| - state = scriptDataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEndTagOpenState() {
|
| - var data = stream.char();
|
| - if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - state = scriptDataEndTagNameState;
|
| - } else {
|
| - _addToken(new CharactersToken("</"));
|
| - stream.unget(data);
|
| - state = scriptDataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEndTagNameState() {
|
| - var appropriate = _tokenIsAppropriate();
|
| - var data = stream.char();
|
| - if (isWhitespace(data) && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = beforeAttributeNameState;
|
| - } else if (data == "/" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = selfClosingStartTagState;
|
| - } else if (data == ">" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - emitCurrentToken();
|
| - state = dataState;
|
| - } else if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - _addToken(new CharactersToken("</$temporaryBuffer"));
|
| - stream.unget(data);
|
| - state = scriptDataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapeStartState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataEscapeStartDashState;
|
| - } else {
|
| - stream.unget(data);
|
| - state = scriptDataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapeStartDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataEscapedDashDashState;
|
| - } else {
|
| - stream.unget(data);
|
| - state = scriptDataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataEscapedDashState;
|
| - } else if (data == "<") {
|
| - state = scriptDataEscapedLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else if (data == EOF) {
|
| - state = dataState;
|
| - } else {
|
| - var chars = stream.charsUntil("<-\u0000");
|
| - _addToken(new CharactersToken("${data}${chars}"));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataEscapedDashDashState;
|
| - } else if (data == "<") {
|
| - state = scriptDataEscapedLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - state = scriptDataEscapedState;
|
| - } else if (data == EOF) {
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new CharactersToken(data));
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedDashDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - } else if (data == "<") {
|
| - state = scriptDataEscapedLessThanSignState;
|
| - } else if (data == ">") {
|
| - _addToken(new CharactersToken(">"));
|
| - state = scriptDataState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - state = scriptDataEscapedState;
|
| - } else if (data == EOF) {
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new CharactersToken(data));
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedLessThanSignState() {
|
| - var data = stream.char();
|
| - if (data == "/") {
|
| - temporaryBuffer = "";
|
| - state = scriptDataEscapedEndTagOpenState;
|
| - } else if (isLetter(data)) {
|
| - _addToken(new CharactersToken("<$data"));
|
| - temporaryBuffer = data;
|
| - state = scriptDataDoubleEscapeStartState;
|
| - } else {
|
| - _addToken(new CharactersToken("<"));
|
| - stream.unget(data);
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedEndTagOpenState() {
|
| - var data = stream.char();
|
| - if (isLetter(data)) {
|
| - temporaryBuffer = data;
|
| - state = scriptDataEscapedEndTagNameState;
|
| - } else {
|
| - _addToken(new CharactersToken("</"));
|
| - stream.unget(data);
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataEscapedEndTagNameState() {
|
| - var appropriate = _tokenIsAppropriate();
|
| - var data = stream.char();
|
| - if (isWhitespace(data) && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = beforeAttributeNameState;
|
| - } else if (data == "/" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - state = selfClosingStartTagState;
|
| - } else if (data == ">" && appropriate) {
|
| - currentToken = new EndTagToken(temporaryBuffer);
|
| - emitCurrentToken();
|
| - state = dataState;
|
| - } else if (isLetter(data)) {
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - _addToken(new CharactersToken("</$temporaryBuffer"));
|
| - stream.unget(data);
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataDoubleEscapeStartState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data) || data == "/" || data == ">") {
|
| - _addToken(new CharactersToken(data));
|
| - if (temporaryBuffer.toLowerCase() == "script") {
|
| - state = scriptDataDoubleEscapedState;
|
| - } else {
|
| - state = scriptDataEscapedState;
|
| - }
|
| - } else if (isLetter(data)) {
|
| - _addToken(new CharactersToken(data));
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - stream.unget(data);
|
| - state = scriptDataEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataDoubleEscapedState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataDoubleEscapedDashState;
|
| - } else if (data == "<") {
|
| - _addToken(new CharactersToken("<"));
|
| - state = scriptDataDoubleEscapedLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-script-in-script"));
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new CharactersToken(data));
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataDoubleEscapedDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - state = scriptDataDoubleEscapedDashDashState;
|
| - } else if (data == "<") {
|
| - _addToken(new CharactersToken("<"));
|
| - state = scriptDataDoubleEscapedLessThanSignState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - state = scriptDataDoubleEscapedState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-script-in-script"));
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new CharactersToken(data));
|
| - state = scriptDataDoubleEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - // TODO(jmesserly): report bug in original code
|
| - // (was "Dash" instead of "DashDash")
|
| - bool scriptDataDoubleEscapedDashDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - _addToken(new CharactersToken("-"));
|
| - } else if (data == "<") {
|
| - _addToken(new CharactersToken("<"));
|
| - state = scriptDataDoubleEscapedLessThanSignState;
|
| - } else if (data == ">") {
|
| - _addToken(new CharactersToken(">"));
|
| - state = scriptDataState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addToken(new CharactersToken("\uFFFD"));
|
| - state = scriptDataDoubleEscapedState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-script-in-script"));
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new CharactersToken(data));
|
| - state = scriptDataDoubleEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataDoubleEscapedLessThanSignState() {
|
| - var data = stream.char();
|
| - if (data == "/") {
|
| - _addToken(new CharactersToken("/"));
|
| - temporaryBuffer = "";
|
| - state = scriptDataDoubleEscapeEndState;
|
| - } else {
|
| - stream.unget(data);
|
| - state = scriptDataDoubleEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool scriptDataDoubleEscapeEndState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data) || data == "/" || data == ">") {
|
| - _addToken(new CharactersToken(data));
|
| - if (temporaryBuffer.toLowerCase() == "script") {
|
| - state = scriptDataEscapedState;
|
| - } else {
|
| - state = scriptDataDoubleEscapedState;
|
| - }
|
| - } else if (isLetter(data)) {
|
| - _addToken(new CharactersToken(data));
|
| - temporaryBuffer = '${temporaryBuffer}$data';
|
| - } else {
|
| - stream.unget(data);
|
| - state = scriptDataDoubleEscapedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool beforeAttributeNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - stream.charsUntil(spaceCharacters, true);
|
| - } else if (isLetter(data)) {
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - } else if (data == ">") {
|
| - emitCurrentToken();
|
| - } else if (data == "/") {
|
| - state = selfClosingStartTagState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("expected-attribute-name-but-got-eof"));
|
| - state = dataState;
|
| - } else if ("'\"=<".contains(data)) {
|
| - _addToken(new ParseErrorToken("invalid-character-in-attribute-name"));
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addAttribute("\uFFFD");
|
| - state = attributeNameState;
|
| - } else {
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool attributeNameState() {
|
| - var data = stream.char();
|
| - bool leavingThisState = true;
|
| - bool emitToken = false;
|
| - if (data == "=") {
|
| - state = beforeAttributeValueState;
|
| - } else if (isLetter(data)) {
|
| - _attributeName = '$_attributeName$data'
|
| - '${stream.charsUntil(asciiLetters, true)}';
|
| - leavingThisState = false;
|
| - } else if (data == ">") {
|
| - // XXX If we emit here the attributes are converted to a dict
|
| - // without being checked and when the code below runs we error
|
| - // because data is a dict not a list
|
| - emitToken = true;
|
| - } else if (isWhitespace(data)) {
|
| - state = afterAttributeNameState;
|
| - } else if (data == "/") {
|
| - state = selfClosingStartTagState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _attributeName = '${_attributeName}\uFFFD';
|
| - leavingThisState = false;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-attribute-name"));
|
| - state = dataState;
|
| - } else if ("'\"<".contains(data)) {
|
| - _addToken(new ParseErrorToken("invalid-character-in-attribute-name"));
|
| - _attributeName = '$_attributeName$data';
|
| - leavingThisState = false;
|
| - } else {
|
| - _attributeName = '$_attributeName$data';
|
| - leavingThisState = false;
|
| - }
|
| -
|
| - if (leavingThisState) {
|
| - _markAttributeNameEnd(-1);
|
| -
|
| - // Attributes are not dropped at this stage. That happens when the
|
| - // start tag token is emitted so values can still be safely appended
|
| - // to attributes, but we do want to report the parse error in time.
|
| - if (lowercaseAttrName) {
|
| - _attributeName = asciiUpper2Lower(_attributeName);
|
| - }
|
| - if (_attributeNames == null) _attributeNames = new Set();
|
| - if (_attributeNames.contains(_attributeName)) {
|
| - _addToken(new ParseErrorToken("duplicate-attribute"));
|
| - }
|
| - _attributeNames.add(_attributeName);
|
| -
|
| - // XXX Fix for above XXX
|
| - if (emitToken) {
|
| - emitCurrentToken();
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterAttributeNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - stream.charsUntil(spaceCharacters, true);
|
| - } else if (data == "=") {
|
| - state = beforeAttributeValueState;
|
| - } else if (data == ">") {
|
| - emitCurrentToken();
|
| - } else if (isLetter(data)) {
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - } else if (data == "/") {
|
| - state = selfClosingStartTagState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _addAttribute("\uFFFD");
|
| - state = attributeNameState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("expected-end-of-tag-but-got-eof"));
|
| - state = dataState;
|
| - } else if ("'\"<".contains(data)) {
|
| - _addToken(new ParseErrorToken("invalid-character-after-attribute-name"));
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - } else {
|
| - _addAttribute(data);
|
| - state = attributeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool beforeAttributeValueState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - stream.charsUntil(spaceCharacters, true);
|
| - } else if (data == "\"") {
|
| - _markAttributeValueStart(0);
|
| - state = attributeValueDoubleQuotedState;
|
| - } else if (data == "&") {
|
| - state = attributeValueUnQuotedState;
|
| - stream.unget(data);
|
| - _markAttributeValueStart(0);
|
| - } else if (data == "'") {
|
| - _markAttributeValueStart(0);
|
| - state = attributeValueSingleQuotedState;
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-attribute-value-but-got-right-bracket"));
|
| - emitCurrentToken();
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _markAttributeValueStart(-1);
|
| - _attributeValue = '${_attributeValue}\uFFFD';
|
| - state = attributeValueUnQuotedState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("expected-attribute-value-but-got-eof"));
|
| - state = dataState;
|
| - } else if ("=<`".contains(data)) {
|
| - _addToken(new ParseErrorToken("equals-in-unquoted-attribute-value"));
|
| - _markAttributeValueStart(-1);
|
| - _attributeValue = '$_attributeValue$data';
|
| - state = attributeValueUnQuotedState;
|
| - } else {
|
| - _markAttributeValueStart(-1);
|
| - _attributeValue = '$_attributeValue$data';
|
| - state = attributeValueUnQuotedState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool attributeValueDoubleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == "\"") {
|
| - _markAttributeValueEnd(-1);
|
| - _markAttributeEnd(0);
|
| - state = afterAttributeValueState;
|
| - } else if (data == "&") {
|
| - processEntityInAttribute('"');
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _attributeValue = '${_attributeValue}\uFFFD';
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-attribute-value-double-quote"));
|
| - _markAttributeValueEnd(-1);
|
| - state = dataState;
|
| - } else {
|
| - _attributeValue = '$_attributeValue$data${stream.charsUntil("\"&")}';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool attributeValueSingleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == "'") {
|
| - _markAttributeValueEnd(-1);
|
| - _markAttributeEnd(0);
|
| - state = afterAttributeValueState;
|
| - } else if (data == "&") {
|
| - processEntityInAttribute("'");
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _attributeValue = '${_attributeValue}\uFFFD';
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-attribute-value-single-quote"));
|
| - _markAttributeValueEnd(-1);
|
| - state = dataState;
|
| - } else {
|
| - _attributeValue = '$_attributeValue$data${stream.charsUntil("\'&")}';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool attributeValueUnQuotedState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - _markAttributeValueEnd(-1);
|
| - state = beforeAttributeNameState;
|
| - } else if (data == "&") {
|
| - processEntityInAttribute(">");
|
| - } else if (data == ">") {
|
| - _markAttributeValueEnd(-1);
|
| - emitCurrentToken();
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-attribute-value-no-quotes"));
|
| - _markAttributeValueEnd(-1);
|
| - state = dataState;
|
| - } else if ('"\'=<`'.contains(data)) {
|
| - _addToken(new ParseErrorToken(
|
| - "unexpected-character-in-unquoted-attribute-value"));
|
| - _attributeValue = '$_attributeValue$data';
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - _attributeValue = '${_attributeValue}\uFFFD';
|
| - } else {
|
| - _attributeValue = '$_attributeValue$data'
|
| - '${stream.charsUntil("&>\"\'=<`$spaceCharacters")}';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterAttributeValueState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = beforeAttributeNameState;
|
| - } else if (data == ">") {
|
| - emitCurrentToken();
|
| - } else if (data == "/") {
|
| - state = selfClosingStartTagState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("unexpected-EOF-after-attribute-value"));
|
| - stream.unget(data);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken(
|
| - "unexpected-character-after-attribute-value"));
|
| - stream.unget(data);
|
| - state = beforeAttributeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool selfClosingStartTagState() {
|
| - var data = stream.char();
|
| - if (data == ">") {
|
| - currentTagToken.selfClosing = true;
|
| - emitCurrentToken();
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("unexpected-EOF-after-solidus-in-tag"));
|
| - stream.unget(data);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken(
|
| - "unexpected-character-after-soldius-in-tag"));
|
| - stream.unget(data);
|
| - state = beforeAttributeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool bogusCommentState() {
|
| - // Make a new comment token and give it as value all the characters
|
| - // until the first > or EOF (charsUntil checks for EOF automatically)
|
| - // and emit it.
|
| - var data = stream.charsUntil(">");
|
| - data = data.replaceAll("\u0000", "\uFFFD");
|
| - _addToken(new CommentToken(data));
|
| -
|
| - // Eat the character directly after the bogus comment which is either a
|
| - // ">" or an EOF.
|
| - stream.char();
|
| - state = dataState;
|
| - return true;
|
| - }
|
| -
|
| - bool markupDeclarationOpenState() {
|
| - var charStack = [stream.char()];
|
| - if (charStack.last == "-") {
|
| - charStack.add(stream.char());
|
| - if (charStack.last == "-") {
|
| - currentToken = new CommentToken("");
|
| - state = commentStartState;
|
| - return true;
|
| - }
|
| - } else if (charStack.last == 'd' || charStack.last == 'D') {
|
| - var matched = true;
|
| - for (var expected in const ['oO', 'cC', 'tT', 'yY', 'pP', 'eE']) {
|
| - var char = stream.char();
|
| - charStack.add(char);
|
| - if (char == EOF || !expected.contains(char)) {
|
| - matched = false;
|
| - break;
|
| - }
|
| - }
|
| - if (matched) {
|
| - currentToken = new DoctypeToken(correct: true);
|
| - state = doctypeState;
|
| - return true;
|
| - }
|
| - } else if (charStack.last == "[" &&
|
| - parser != null && parser.tree.openElements.length > 0 &&
|
| - parser.tree.openElements.last.namespaceUri
|
| - != parser.tree.defaultNamespace) {
|
| - var matched = true;
|
| - for (var expected in const ["C", "D", "A", "T", "A", "["]) {
|
| - charStack.add(stream.char());
|
| - if (charStack.last != expected) {
|
| - matched = false;
|
| - break;
|
| - }
|
| - }
|
| - if (matched) {
|
| - state = cdataSectionState;
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - _addToken(new ParseErrorToken("expected-dashes-or-doctype"));
|
| -
|
| - while (charStack.length > 0) {
|
| - stream.unget(charStack.removeLast());
|
| - }
|
| - state = bogusCommentState;
|
| - return true;
|
| - }
|
| -
|
| - bool commentStartState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - state = commentStartDashState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = '${currentStringToken.data}\uFFFD';
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("incorrect-comment"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentStringToken.data = '${currentStringToken.data}$data';
|
| - state = commentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool commentStartDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - state = commentEndState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = '${currentStringToken.data}-\uFFFD';
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("incorrect-comment"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentStringToken.data = '${currentStringToken.data}-${data}';
|
| - state = commentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool commentState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - state = commentEndDashState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = '${currentStringToken.data}\uFFFD';
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentStringToken.data = '${currentStringToken.data}$data'
|
| - '${stream.charsUntil("-\u0000")}';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool commentEndDashState() {
|
| - var data = stream.char();
|
| - if (data == "-") {
|
| - state = commentEndState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = "${currentStringToken.data}-\uFFFD";
|
| - state = commentState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment-end-dash"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentStringToken.data = "${currentStringToken.data}-${data}";
|
| - state = commentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool commentEndState() {
|
| - var data = stream.char();
|
| - if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = '${currentStringToken.data}--\uFFFD';
|
| - state = commentState;
|
| - } else if (data == "!") {
|
| - _addToken(new ParseErrorToken(
|
| - "unexpected-bang-after-double-dash-in-comment"));
|
| - state = commentEndBangState;
|
| - } else if (data == "-") {
|
| - _addToken(new ParseErrorToken(
|
| - "unexpected-dash-after-double-dash-in-comment"));
|
| - currentStringToken.data = '${currentStringToken.data}$data';
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment-double-dash"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - // XXX
|
| - _addToken(new ParseErrorToken("unexpected-char-in-comment"));
|
| - currentStringToken.data = "${currentStringToken.data}--${data}";
|
| - state = commentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool commentEndBangState() {
|
| - var data = stream.char();
|
| - if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == "-") {
|
| - currentStringToken.data = '${currentStringToken.data}--!';
|
| - state = commentEndDashState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentStringToken.data = '${currentStringToken.data}--!\uFFFD';
|
| - state = commentState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-comment-end-bang-state"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentStringToken.data = "${currentStringToken.data}--!${data}";
|
| - state = commentState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypeState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = beforeDoctypeNameState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-doctype-name-but-got-eof"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("need-space-after-doctype"));
|
| - stream.unget(data);
|
| - state = beforeDoctypeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool beforeDoctypeNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-doctype-name-but-got-right-bracket"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.name = "\uFFFD";
|
| - state = doctypeNameState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken(
|
| - "expected-doctype-name-but-got-eof"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.name = data;
|
| - state = doctypeNameState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypeNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
|
| - state = afterDoctypeNameState;
|
| - } else if (data == ">") {
|
| - currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.name = "${currentDoctypeToken.name}\uFFFD";
|
| - state = doctypeNameState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype-name"));
|
| - currentDoctypeToken.correct = false;
|
| - currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.name = '${currentDoctypeToken.name}$data';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterDoctypeNameState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - currentDoctypeToken.correct = false;
|
| - stream.unget(data);
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - if (data == "p" || data == "P") {
|
| - // TODO(jmesserly): would be nice to have a helper for this.
|
| - var matched = true;
|
| - for (var expected in const ["uU", "bB", "lL", "iI", "cC"]) {
|
| - data = stream.char();
|
| - if (data == EOF || !expected.contains(data)) {
|
| - matched = false;
|
| - break;
|
| - }
|
| - }
|
| - if (matched) {
|
| - state = afterDoctypePublicKeywordState;
|
| - return true;
|
| - }
|
| - } else if (data == "s" || data == "S") {
|
| - var matched = true;
|
| - for (var expected in const ["yY", "sS", "tT", "eE", "mM"]) {
|
| - data = stream.char();
|
| - if (data == EOF || !expected.contains(data)) {
|
| - matched = false;
|
| - break;
|
| - }
|
| - }
|
| - if (matched) {
|
| - state = afterDoctypeSystemKeywordState;
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - // All the characters read before the current 'data' will be
|
| - // [a-zA-Z], so they're garbage in the bogus doctype and can be
|
| - // discarded; only the latest character might be '>' or EOF
|
| - // and needs to be ungetted
|
| - stream.unget(data);
|
| - _addToken(new ParseErrorToken(
|
| - "expected-space-or-right-bracket-in-doctype",
|
| - messageParams: {"data": data}));
|
| - currentDoctypeToken.correct = false;
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterDoctypePublicKeywordState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = beforeDoctypePublicIdentifierState;
|
| - } else if (data == "'" || data == '"') {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - stream.unget(data);
|
| - state = beforeDoctypePublicIdentifierState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - stream.unget(data);
|
| - state = beforeDoctypePublicIdentifierState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool beforeDoctypePublicIdentifierState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == "\"") {
|
| - currentDoctypeToken.publicId = "";
|
| - state = doctypePublicIdentifierDoubleQuotedState;
|
| - } else if (data == "'") {
|
| - currentDoctypeToken.publicId = "";
|
| - state = doctypePublicIdentifierSingleQuotedState;
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypePublicIdentifierDoubleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == '"') {
|
| - state = afterDoctypePublicIdentifierState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.publicId = "${currentDoctypeToken.publicId}\uFFFD";
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypePublicIdentifierSingleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == "'") {
|
| - state = afterDoctypePublicIdentifierState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.publicId = "${currentDoctypeToken.publicId}\uFFFD";
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterDoctypePublicIdentifierState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = betweenDoctypePublicAndSystemIdentifiersState;
|
| - } else if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == '"') {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierDoubleQuotedState;
|
| - } else if (data == "'") {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierSingleQuotedState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool betweenDoctypePublicAndSystemIdentifiersState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == '"') {
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierDoubleQuotedState;
|
| - } else if (data == "'") {
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierSingleQuotedState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterDoctypeSystemKeywordState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - state = beforeDoctypeSystemIdentifierState;
|
| - } else if (data == "'" || data == '"') {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - stream.unget(data);
|
| - state = beforeDoctypeSystemIdentifierState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - stream.unget(data);
|
| - state = beforeDoctypeSystemIdentifierState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool beforeDoctypeSystemIdentifierState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == "\"") {
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierDoubleQuotedState;
|
| - } else if (data == "'") {
|
| - currentDoctypeToken.systemId = "";
|
| - state = doctypeSystemIdentifierSingleQuotedState;
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypeSystemIdentifierDoubleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == "\"") {
|
| - state = afterDoctypeSystemIdentifierState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.systemId = "${currentDoctypeToken.systemId}\uFFFD";
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool doctypeSystemIdentifierSingleQuotedState() {
|
| - var data = stream.char();
|
| - if (data == "'") {
|
| - state = afterDoctypeSystemIdentifierState;
|
| - } else if (data == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - currentDoctypeToken.systemId = "${currentDoctypeToken.systemId}\uFFFD";
|
| - } else if (data == ">") {
|
| - _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool afterDoctypeSystemIdentifierState() {
|
| - var data = stream.char();
|
| - if (isWhitespace(data)) {
|
| - return true;
|
| - } else if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - _addToken(new ParseErrorToken("eof-in-doctype"));
|
| - currentDoctypeToken.correct = false;
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else {
|
| - _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
|
| - state = bogusDoctypeState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool bogusDoctypeState() {
|
| - var data = stream.char();
|
| - if (data == ">") {
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - } else if (data == EOF) {
|
| - // XXX EMIT
|
| - stream.unget(data);
|
| - _addToken(currentToken);
|
| - state = dataState;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool cdataSectionState() {
|
| - var data = [];
|
| - int matchedEnd = 0;
|
| - while (true) {
|
| - var ch = stream.char();
|
| - if (ch == EOF) {
|
| - break;
|
| - }
|
| - // Deal with null here rather than in the parser
|
| - if (ch == "\u0000") {
|
| - _addToken(new ParseErrorToken("invalid-codepoint"));
|
| - ch = "\uFFFD";
|
| - }
|
| - data.add(ch);
|
| - // TODO(jmesserly): it'd be nice if we had an easier way to match the end,
|
| - // perhaps with a "peek" API.
|
| - if (ch == "]" && matchedEnd < 2) {
|
| - matchedEnd++;
|
| - } else if (ch == ">" && matchedEnd == 2) {
|
| - // Remove "]]>" from the end.
|
| - data.removeLast();
|
| - data.removeLast();
|
| - data.removeLast();
|
| - break;
|
| - } else {
|
| - matchedEnd = 0;
|
| - }
|
| - }
|
| -
|
| - if (data.length > 0) {
|
| - _addToken(new CharactersToken(data.join()));
|
| - }
|
| - state = dataState;
|
| - return true;
|
| - }
|
| -}
|
| -
|
|
|