scroll-timeline-polyfill
Version:
A polyfill for scroll-driven animations on the web via ScrollTimeline
799 lines (734 loc) • 28.3 kB
JavaScript
export class Token {}
// The output of tokenization step is a stream of zero or more of the following tokens: <ident-token>, <function-token>,
// <at-keyword-token>, <hash-token>, <string-token>, <bad-string-token>, <url-token>, <bad-url-token>, <delim-token>,
// <number-token>, <percentage-token>, <dimension-token>, <whitespace-token>, <CDO-token>, <CDC-token>, <colon-token>,
// <semicolon-token>, <comma-token>, <[-token>, <]-token>, <(-token>, <)-token>, <{-token>, and <}-token>.
export class IdentToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class FunctionToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class AtKeywordToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class HashToken extends Token {
type;
value;
constructor(value, type = 'unrestricted') {
super();
this.value = value;
this.type = type;
}
}
export class StringToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class BadStringToken extends Token {}
export class UrlToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class BadUrlToken extends Token {}
export class DelimToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class NumberToken extends Token {
value;
type;
constructor(value, type = "integer") {
super();
this.value = value;
this.type = type;
}
}
export class PercentageToken extends Token {
value;
constructor(value) {
super();
this.value = value;
}
}
export class DimensionToken extends Token {
value;
type;
unit;
constructor(value, type, unit) {
super();
this.value = value;
this.type = type;
this.unit = unit;
}
}
export class WhitespaceToken extends Token {}
export class CDOToken extends Token {}
export class CDCToken extends Token {}
export class ColonToken extends Token {}
export class SemicolonToken extends Token {}
export class CommaToken extends Token {}
export class LeftSquareBracketToken extends Token {}
export class RightSquareBracketToken extends Token {}
export class LeftParenthesisToken extends Token {}
export class RightParenthesisToken extends Token {}
export class LeftCurlyBracketToken extends Token {}
export class RightCurlyBracketToken extends Token {}
class InputStream {
input
index = 0;
constructor(input) {
this.input = input;
}
consume() {
const codePoint = this.input.codePointAt(this.index);
if (typeof codePoint !== 'undefined') {
this.index += String.fromCodePoint(codePoint).length;
}
return codePoint;
}
reconsume(codePoint) {
if (typeof codePoint !== 'undefined') {
this.index -= String.fromCodePoint(codePoint).length
}
}
peek() {
const codePoints = []
let position = this.index
for (let i = 0; i < 3 && position < this.input.length; i++) {
const nextCodePoint = this.input.codePointAt(position);
codePoints.push(nextCodePoint);
position += String.fromCodePoint(nextCodePoint).length;
}
return codePoints;
}
}
function isNewline(codePoint) {
// U+000A LINE FEED.
return codePoint === 0x000A;
}
function isWhitespace(codePoint) {
// A newline, U+0009 CHARACTER TABULATION, or U+0020 SPACE.
return isNewline(codePoint) || codePoint === 0x2000 || codePoint === 0x0020;
}
function isDigit(codePoint) {
// A code point between U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) inclusive.
return codePoint >= 0x0030 && codePoint <=0x0039;
}
function isHexDigit(codePoint) {
// A digit, or a code point between U+0041 LATIN CAPITAL LETTER A (A) and U+0046 LATIN CAPITAL LETTER F (F) inclusive,
// or a code point between U+0061 LATIN SMALL LETTER A (a) and U+0066 LATIN SMALL LETTER F (f) inclusive.
return isDigit(codePoint) ||
(codePoint >= 0x0041 && codePoint <= 0x0046) ||
(codePoint >= 0x0061 && codePoint <= 0x0066);
}
function isUppercaseLetter(codePoint) {
// A code point between U+0041 LATIN CAPITAL LETTER A (A) and U+005A LATIN CAPITAL LETTER Z (Z) inclusive.
return codePoint >= 0x0041 && codePoint <= 0x005A;
}
function isLowercaseLetter(codePoint) {
// A code point between U+0061 LATIN SMALL LETTER A (a) and U+007A LATIN SMALL LETTER Z (z) inclusive.
return codePoint >= 0x0061 && codePoint <= 0x007A;
}
function isLetter(codePoint) {
// An uppercase letter or a lowercase letter.
return isUppercaseLetter(codePoint) || isLowercaseLetter(codePoint);
}
function nonASCIICodePoint(codePoint) {
// A code point with a value equal to or greater than U+0080 <control>.
return codePoint >= 0x0080;
}
function isIdentStartCodePoint(codePoint) {
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
return isLetter(codePoint) || nonASCIICodePoint(codePoint) || codePoint === 0x005F;
}
function isIdentCodePoint(codePoint) {
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).
return isIdentStartCodePoint(codePoint) || isDigit(codePoint) || codePoint === 0x002D;
}
function isNonPrintableCodePoint(codePoint) {
// A code point between U+0000 NULL and U+0008 BACKSPACE inclusive, or U+000B LINE TABULATION,
// or a code point between U+000E SHIFT OUT and U+001F INFORMATION SEPARATOR ONE inclusive, or U+007F DELETE.
return (codePoint >= 0x0000 && codePoint <= 0x0008) || codePoint === 0x000B ||
(codePoint >= 0x000E && codePoint <= 0x001F) || codePoint === 0x007F;
}
function validEscape(firstCodePoint, secondCodePoint) {
// If the first code point is not U+005C REVERSE SOLIDUS (\), return false.
// Otherwise, if the second code point is a newline, return false.
// Otherwise, return true.
return firstCodePoint === 0x005C && !isNewline(secondCodePoint);
}
function startsIdentSequence(firstCodePoint, secondCodePoint, thirdCodePoint) {
// Look at the first code point:
if (firstCodePoint === 0x002D) {
// U+002D HYPHEN-MINUS
// If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS,
// or the second and third code points are a valid escape, return true. Otherwise, return false.
return isIdentStartCodePoint(secondCodePoint) || secondCodePoint === 0x002D ||
validEscape(secondCodePoint, thirdCodePoint);
} else if (isIdentStartCodePoint(firstCodePoint)) {
// ident-start code point
// Return true.
return true;
} else if (firstCodePoint === 0x005C) {
// U+005C REVERSE SOLIDUS (\)
// If the first and second code points are a valid escape, return true. Otherwise, return false.
return validEscape(firstCodePoint, secondCodePoint);
} else {
// anything else
// Return false.
return false;
}
}
function startsNumber(firstCodePoint, secondCodePoint, thirdCodePoint) {
// https://www.w3.org/TR/css-syntax-3/#check-if-three-code-points-would-start-a-number
// Look at the first code point:
if (firstCodePoint === 0x002B || firstCodePoint === 0x002D) {
// U+002B PLUS SIGN (+)
// U+002D HYPHEN-MINUS (-)
// If the second code point is a digit, return true.
// Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code point is a digit, return true.
//
// Otherwise, return false.
return isDigit(secondCodePoint) || (secondCodePoint === 0x002E && isDigit(thirdCodePoint));
} else if (firstCodePoint === 0x002E) {
// U+002E FULL STOP (.)
// If the second code point is a digit, return true. Otherwise, return false.
return isDigit(secondCodePoint);
} else {
// digit
// Return true.
// anything else
// Return false.
return isDigit(firstCodePoint);
}
}
/**
* Consume an escaped code point
* https://www.w3.org/TR/css-syntax-3/#consume-an-escaped-code-point
*
* @param {InputStream} input
* @return number
*/
function consumeEscapedCodePoint(input) {
// Consume the next input code point.
const codePoint = input.consume();
if (isHexDigit(codePoint)) {
let digits = [codePoint];
// hex digit
// Consume as many hex digits as possible, but no more than 5. Note that this means 1-6 hex digits have been
// consumed in total.
while(isHexDigit(...input.peek()) && digits.length < 5) {
digits.push(input.consume());
}
// If the next input code point is whitespace, consume it as well.
if (isWhitespace(...input.peek())) {
input.consume();
}
// Interpret the hex digits as a hexadecimal number. If this number is zero, or is for a surrogate, or is greater
// than the maximum allowed code point, return U+FFFD REPLACEMENT CHARACTER (�). Otherwise, return the code point
// with that value.
const number = parseInt(String.fromCodePoint(...digits), 16);
if (number === 0 || number > 0x10FFFF) {
return 0xFFFD;
} else {
return number;
}
} else if (typeof codePoint === 'undefined') {
// EOF
// This is a parse error. Return U+FFFD REPLACEMENT CHARACTER (�).
return 0xFFFD;
} else {
// anything else
// Return the current input code point.
return codePoint;
}
}
/**
* Consume a string token
* https://www.w3.org/TR/css-syntax-3/#consume-a-string-token
*
* @param {InputStream} input
* @param {number} endingCodePoint
*/
function consumeStringToken(input, endingCodePoint) {
const stringToken = new StringToken('');
while (true) {
// Repeatedly consume the next input code point from the stream:
const codePoint = input.consume();
if (codePoint === endingCodePoint) {
// ending code point
// Return the <string-token>.
return stringToken;
} else if (typeof codePoint === 'undefined') {
// EOF
// This is a parse error. Return the <string-token>.
return stringToken
} else if (codePoint === 0x00A) {
// newline
// This is a parse error. Reconsume the current input code point, create a <bad-string-token>, and return it.
input.reconsume(codePoint);
return new BadStringToken();
} else if (codePoint === 0x005C) {
// U+005C REVERSE SOLIDUS (\)
const nextCodePoint = input.peek()[0];
if (typeof nextCodePoint === 'undefined') {
// If the next input code point is EOF, do nothing.
} else if (isNewline(nextCodePoint)) {
// Otherwise, if the next input code point is a newline, consume it.
input.consume();
} else {
// Otherwise, (the stream starts with a valid escape) consume an escaped code point and
// append the returned code point to the <string-token>’s value.
stringToken.value += String.fromCodePoint(consumeEscapedCodePoint(input));
}
} else {
// anything else
// Append the current input code point to the <string-token>’s value.
stringToken.value += String.fromCodePoint(codePoint);
}
}
}
/**
* Consume ident sequence
* https://www.w3.org/TR/css-syntax-3/#consume-name
*
* @param {InputStream} input
*/
function consumeIdentSequence(input) {
// Let result initially be an empty string.
let result = '';
// Repeatedly consume the next input code point from the stream:
while (true) {
const codePoint = input.consume();
if (isIdentCodePoint(codePoint)) {
// ident code point
// Append the code point to result.
result += String.fromCodePoint(codePoint);
} else if (validEscape(...input.peek())) {
// the stream starts with a valid escape
// Consume an escaped code point. Append the returned code point to result.
result += String.fromCodePoint(consumeEscapedCodePoint(input));
} else {
// anything else
// Reconsume the current input code point. Return result.
input.reconsume(codePoint);
return result;
}
}
}
/**
* Consume a number
* https://www.w3.org/TR/css-syntax-3/#consume-a-number
*
* @param {InputStream} input
*/
function consumeNumber(input) {
// Execute the following steps in order:
//
// Initially set type to "integer". Let repr be the empty string.
let type = 'integer';
let repr = '';
// If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr.
if ([0x002B, 0x002D].includes(input.peek()[0])) {
repr += String.fromCodePoint(input.consume());
}
// While the next input code point is a digit, consume it and append it to repr.
while(isDigit(...input.peek())) {
repr += String.fromCodePoint(input.consume());
}
// If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
// Consume them.
// Append them to repr.
// Set type to "number".
// While the next input code point is a digit, consume it and append it to repr.
if (input.peek()[0] === 0x002E && isDigit(input.peek()[1])) {
repr += String.fromCodePoint(input.consume(), input.consume());
type = 'number';
while(isDigit(...input.peek())) {
repr += String.fromCodePoint(input.consume());
}
}
// If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e),
// optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+),
// followed by a digit, then:
// Consume them.
// Append them to repr.
// Set type to "number".
// While the next input code point is a digit, consume it and append it to repr.
if ([0x0045, 0x0065].includes(input.peek()[0])) {
if ([0x002D, 0x002B].includes(input.peek()[1]) && isDigit(input.peek()[2])) {
repr += String.fromCodePoint(input.consume(), input.consume(), input.consume());
type = 'number';
} else if (isDigit(input.peek()[1])) {
repr += String.fromCodePoint(input.consume(), input.consume());
type = 'number';
}
}
// Convert repr to a number, and set the value to the returned value.
const value = parseFloat(repr);
// Return value and type.
return { value, type };
}
/**
* Consume a numeric token
* https://www.w3.org/TR/css-syntax-3/#consume-a-numeric-token
*
* @param {InputStream} input
*/
function consumeNumericToken(input) {
// Consume a number and let number be the result.
let number = consumeNumber(input);
// If the next 3 input code points would start an ident sequence, then:
if (startsIdentSequence(...input.peek())) {
// Create a <dimension-token> with the same value and type flag as number, and a unit set initially to the empty string.
// Consume an ident sequence. Set the <dimension-token>’s unit to the returned value.
// Return the <dimension-token>.
return new DimensionToken(number.value, number.type, consumeIdentSequence(input));
} else if (input.peek()[0] === 0x0025) {
// Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it.
// Create a <percentage-token> with the same value as number, and return it.
input.consume();
return new PercentageToken(number.value);
} else {
// Otherwise, create a <number-token> with the same value and type flag as number, and return it.
return new NumberToken(number.value, number.type);
}
}
/**
* Consume remnants of a bad url
* https://www.w3.org/TR/css-syntax-3/#consume-the-remnants-of-a-bad-url
* @param {InputStream} input
*/
function consumeRemnantsOfBadUrl(input) {
// Repeatedly consume the next input code point from the stream:
while (true) {
const codePoint = input.consume();
if (codePoint === 0x0029 || typeof codePoint === 'undefined') {
// U+0029 RIGHT PARENTHESIS ())
// EOF
// Return.
return;
} else if (validEscape(...input.peek())) {
// the input stream starts with a valid escape
// Consume an escaped code point. This allows an escaped right parenthesis ("\)") to be encountered without
// ending the <bad-url-token>. This is otherwise identical to the "anything else" clause.
consumeEscapedCodePoint(input);
}
// anything else
// Do nothing.
}
}
/**
* Consume URL token
* https://www.w3.org/TR/css-syntax-3/#consume-a-url-token
* @param {InputStream} input
*/
function consumeUrlToken(input) {
// Initially create a <url-token> with its value set to the empty string.
const urlToken = new UrlToken('');
// Consume as much whitespace as possible.
while(isWhitespace(...input.peek())) {
input.consume();
}
// Repeatedly consume the next input code point from the stream:
while (true) {
const codePoint = input.consume();
if (codePoint === 0x0029) {
// U+0029 RIGHT PARENTHESIS ())
// Return the <url-token>.
return urlToken;
} else if (typeof codePoint === 'undefined') {
// EOF
// This is a parse error. Return the <url-token>.
return urlToken;
} else if (isWhitespace(codePoint)) {
// whitespace
// Consume as much whitespace as possible.
while(isWhitespace(...input.peek())) {
input.consume();
}
if (input.peek()[0] === 0x0029 || typeof input.peek()[0] === 'undefined') {
// If the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF,
// consume it and return the <url-token> (if EOF was encountered, this is a parse error);
input.consume();
return urlToken;
} else {
// otherwise, consume the remnants of a bad url, create a <bad-url-token>, and return it.
consumeRemnantsOfBadUrl(input);
return new BadUrlToken();
}
} else if ([0x0022, 0x0027, 0x0028].includes(codePoint) || isNonPrintableCodePoint(codePoint)) {
// U+0022 QUOTATION MARK (")
// U+0027 APOSTROPHE (')
// U+0028 LEFT PARENTHESIS (()
// non-printable code point
// This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
consumeRemnantsOfBadUrl(input);
return new BadUrlToken();
} else if (codePoint === 0x005C) {
// U+005C REVERSE SOLIDUS (\)
if (validEscape(...input.peek())) {
// If the stream starts with a valid escape,
// consume an escaped code point and append the returned code point to the <url-token>’s value.
urlToken.value += consumeEscapedCodePoint(input);
} else {
// Otherwise, this is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
consumeRemnantsOfBadUrl(input);
return new BadUrlToken();
}
} else {
// anything else
// Append the current input code point to the <url-token>’s value.
urlToken.value += String.fromCodePoint(codePoint);
}
}
}
/**
* Consume ident like token
* https://www.w3.org/TR/css-syntax-3/#consume-an-ident-like-token
*
* @param {InputStream} input
*/
function consumeIdentLikeToken(input) {
// Consume an ident sequence, and let string be the result.
const str = consumeIdentSequence(input);
if (str.match(/url/i) && input.peek()[0] === 0x0028) {
// If string’s value is an ASCII case-insensitive match for "url",
// and the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
input.consume();
// While the next two input code points are whitespace, consume the next input code point.
while(isWhitespace(input.peek()[0]) && isWhitespace(input.peek()[1])) {
input.consume();
}
if ([0x0022, 0x0027].includes(input.peek()[0]) ||
(isWhitespace(input.peek()[0]) && [0x0022, 0x0027].includes(input.peek()[1]))) {
// If the next one or two input code points are U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE ('),
// or whitespace followed by U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE ('),
// then create a <function-token> with its value set to string and return it.
return new FunctionToken(str);
} else {
// Otherwise, consume a url token, and return it.
return consumeUrlToken(input);
}
} else if (input.peek()[0] === 0x0028) {
// Otherwise, if the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
// Create a <function-token> with its value set to string and return it.
input.consume();
return new FunctionToken(str);
} else {
// Otherwise, create an <ident-token> with its value set to string and return it.
return new IdentToken(str);
}
}
/**
* Consume a token.
*
* https://www.w3.org/TR/css-syntax-3/#consume-a-token
*
* @param {InputStream} input
*/
function consumeToken(input) {
// Consume the next input code point
const codePoint = input.consume()
const lookahead = input.peek()
if (isWhitespace(codePoint)) {
// whitespace
// Consume as much whitespace as possible. Return a <whitespace-token>.
while(isWhitespace(...input.peek())) {
input.consume();
}
return new WhitespaceToken();
} else if (codePoint === 0x0022) {
// U+0022 QUOTATION MARK (")
// Consume a string token and return it.
return consumeStringToken(input, codePoint);
} else if (codePoint === 0x0023) {
// U+0023 NUMBER SIGN (#)
// If the next input code point is an ident code point or the next two input code points are a valid escape, then:
// Create a <hash-token>.
// If the next 3 input code points would start an ident sequence, set the <hash-token>’s type flag to "id".
// Consume an ident sequence, and set the <hash-token>’s value to the returned string.
// Return the <hash-token>.
// Otherwise, return a <delim-token> with its value set to the current input code point.
if (isIdentCodePoint(lookahead[0]) || validEscape(...lookahead)) {
const hashToken = new HashToken();
if (startsIdentSequence(...lookahead)) {
hashToken.type = 'id';
}
hashToken.value = consumeIdentSequence(input);
return hashToken;
} else {
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x0027) {
// U+0027 APOSTROPHE (')
// Consume a string token and return it.
return consumeStringToken(input, codePoint);
} else if (codePoint === 0x0028) {
// U+0028 LEFT PARENTHESIS (()
// Return a <(-token>.
return new LeftParenthesisToken();
} else if (codePoint === 0x0029) {
// U+0029 RIGHT PARENTHESIS ())
// Return a <)-token>.
return new RightParenthesisToken();
} else if (codePoint === 0x002B) {
// U+002B PLUS SIGN (+)
// If the input stream starts with a number, reconsume the current input code point, consume a numeric token,
// and return it.
// Otherwise, return a <delim-token> with its value set to the current input code point.
if (startsNumber(...lookahead)) {
input.reconsume(codePoint);
return consumeNumericToken(input);
} else {
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x002C) {
// U+002C COMMA (,)
// Return a <comma-token>.
return new CommaToken();
} else if (codePoint === 0x002D) {
// U+002D HYPHEN-MINUS (-)
if (startsNumber(...input.peek())) {
// If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
input.reconsume(codePoint);
return consumeNumericToken(input);
} else if (input.peek()[0] === 0x002D && input.peek()[1] === 0x003E) {
// Otherwise, if the next 2 input code points are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them and return a <CDC-token>.
input.consume();
input.consume();
return new CDCToken();
} else if (startsIdentSequence(...input.peek())) {
// Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point, consume an ident-like token, and return it.
input.reconsume(codePoint);
return consumeIdentLikeToken(input);
} else {
// Otherwise, return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x002E) {
// U+002E FULL STOP (.)
if (startsNumber(...input.peek())) {
// If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
input.reconsume(codePoint);
return consumeNumericToken(input);
} else {
// Otherwise, return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x003A) {
// U+003A COLON (:)
// Return a <colon-token>.
return new ColonToken();
} else if (codePoint === 0x003B) {
// U+003B SEMICOLON (;)
// Return a <semicolon-token>.
return new SemicolonToken();
} else if (codePoint === 0x003C) {
// U+003C LESS-THAN SIGN (<)
if (lookahead[0] === 0x0021 && lookahead[1] === 0x002D && lookahead[2] === 0x002D) {
// If the next 3 input code points are U+0021 EXCLAMATION MARK U+002D HYPHEN-MINUS U+002D HYPHEN-MINUS (!--), consume them and return a <CDO-token>.
input.consume();
input.consume();
input.consume();
return new CDOToken();
} else {
// Otherwise, return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x0040) {
// U+0040 COMMERCIAL AT (@)
if (startsIdentSequence(...lookahead)) {
// If the next 3 input code points would start an ident sequence, consume an ident sequence,
// create an <at-keyword-token> with its value set to the returned value, and return it.
return new AtKeywordToken(consumeIdentSequence(input));
} else {
// Otherwise, return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x005B) {
// U+005B LEFT SQUARE BRACKET ([)
// Return a <[-token>.
return new LeftSquareBracketToken();
} else if (codePoint === 0x005C) {
// U+005C REVERSE SOLIDUS (\)
if (validEscape(...lookahead)) {
// If the input stream starts with a valid escape, reconsume the current input code point, consume an ident-like token, and return it.
input.reconsume(codePoint);
return consumeIdentLikeToken(input);
} else {
// Otherwise, this is a parse error. Return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
} else if (codePoint === 0x005D) {
// U+005D RIGHT SQUARE BRACKET (])
// Return a <]-token>.
return new RightSquareBracketToken();
} else if (codePoint === 0x007B) {
// U+007B LEFT CURLY BRACKET ({)
// Return a <{-token>.
return new LeftCurlyBracketToken();
} else if (codePoint === 0x007D) {
// U+007D RIGHT CURLY BRACKET (})
// Return a <}-token>.
return new RightCurlyBracketToken();
} else if (isDigit(codePoint)) {
// digit
// Reconsume the current input code point, consume a numeric token, and return it.
input.reconsume(codePoint);
return consumeNumericToken(input);
} else if (isIdentStartCodePoint(codePoint)) {
// ident-start code point
// Reconsume the current input code point, consume an ident-like token, and return it.
input.reconsume(codePoint);
return consumeIdentLikeToken(input);
} else if (typeof codePoint === 'undefined') {
// EOF
// Return an <EOF-token>.
return undefined;
} else {
// anything else
// Return a <delim-token> with its value set to the current input code point.
return new DelimToken(String.fromCodePoint(codePoint));
}
}
/**
* Tokenize a string into an array of CSS tokens.
* @param {string} str
*/
export function tokenizeString(str) {
const input = new InputStream(str);
// To tokenize a stream of code points into a stream of CSS tokens input, repeatedly consume a token from input
// until an <EOF-token> is reached, pushing each of the returned tokens into a stream.
const tokens = [];
while (true) {
const token = consumeToken(input);
if (typeof token === 'undefined') {
return tokens;
} else {
tokens.push(token);
}
}
}