| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";/** * @typedef {Object} CssTokenCallbacks * @property {function(string, number): boolean} isSelector * @property {function(string, number, number, number, number): number=} url * @property {function(string, number, number): number=} string * @property {function(string, number, number): number=} leftParenthesis * @property {function(string, number, number): number=} rightParenthesis * @property {function(string, number, number): number=} pseudoFunction * @property {function(string, number, number): number=} function * @property {function(string, number, number): number=} pseudoClass * @property {function(string, number, number): number=} atKeyword * @property {function(string, number, number): number=} class * @property {function(string, number, number): number=} identifier * @property {function(string, number, number): number=} id * @property {function(string, number, number): number=} leftCurlyBracket * @property {function(string, number, number): number=} rightCurlyBracket * @property {function(string, number, number): number=} semicolon * @property {function(string, number, number): number=} comma *//** @typedef {function(string, number, CssTokenCallbacks): number} CharHandler */// spec: https://drafts.csswg.org/css-syntax/const CC_LINE_FEED = "\n".charCodeAt(0);const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);const CC_FORM_FEED = "\f".charCodeAt(0);const CC_TAB = "\t".charCodeAt(0);const CC_SPACE = " ".charCodeAt(0);const CC_SLASH = "/".charCodeAt(0);const CC_BACK_SLASH = "\\".charCodeAt(0);const CC_ASTERISK = "*".charCodeAt(0);const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);const CC_LEFT_CURLY = "{".charCodeAt(0);const CC_RIGHT_CURLY = "}".charCodeAt(0);const CC_QUOTATION_MARK = '"'.charCodeAt(0);const CC_APOSTROPHE = "'".charCodeAt(0);const CC_FULL_STOP = ".".charCodeAt(0);const CC_COLON = ":".charCodeAt(0);const CC_SEMICOLON = ";".charCodeAt(0);const CC_COMMA = ",".charCodeAt(0);const CC_PERCENTAGE = "%".charCodeAt(0);const CC_AT_SIGN = "@".charCodeAt(0);const CC_LOW_LINE = "_".charCodeAt(0);const CC_LOWER_A = "a".charCodeAt(0);const CC_LOWER_U = "u".charCodeAt(0);const CC_LOWER_E = "e".charCodeAt(0);const CC_LOWER_Z = "z".charCodeAt(0);const CC_UPPER_A = "A".charCodeAt(0);const CC_UPPER_E = "E".charCodeAt(0);const CC_UPPER_Z = "Z".charCodeAt(0);const CC_0 = "0".charCodeAt(0);const CC_9 = "9".charCodeAt(0);const CC_NUMBER_SIGN = "#".charCodeAt(0);const CC_PLUS_SIGN = "+".charCodeAt(0);const CC_HYPHEN_MINUS = "-".charCodeAt(0);const CC_LESS_THAN_SIGN = "<".charCodeAt(0);const CC_GREATER_THAN_SIGN = ">".charCodeAt(0);const _isNewLine = cc => {	return (		cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED	);};/** @type {CharHandler} */const consumeSpace = (input, pos, callbacks) => {	let cc;	do {		pos++;		cc = input.charCodeAt(pos);	} while (_isWhiteSpace(cc));	return pos;};const _isWhiteSpace = cc => {	return (		cc === CC_LINE_FEED ||		cc === CC_CARRIAGE_RETURN ||		cc === CC_FORM_FEED ||		cc === CC_TAB ||		cc === CC_SPACE	);};/** @type {CharHandler} */const consumeSingleCharToken = (input, pos, callbacks) => {	return pos + 1;};/** @type {CharHandler} */const consumePotentialComment = (input, pos, callbacks) => {	pos++;	if (pos === input.length) return pos;	let cc = input.charCodeAt(pos);	if (cc !== CC_ASTERISK) return pos;	for (;;) {		pos++;		if (pos === input.length) return pos;		cc = input.charCodeAt(pos);		while (cc === CC_ASTERISK) {			pos++;			if (pos === input.length) return pos;			cc = input.charCodeAt(pos);			if (cc === CC_SLASH) return pos + 1;		}	}};/** @type {function(number): CharHandler} */const consumeString = end => (input, pos, callbacks) => {	const start = pos;	pos = _consumeString(input, pos, end);	if (callbacks.string !== undefined) {		pos = callbacks.string(input, start, pos);	}	return pos;};const _consumeString = (input, pos, end) => {	pos++;	for (;;) {		if (pos === input.length) return pos;		const cc = input.charCodeAt(pos);		if (cc === end) return pos + 1;		if (_isNewLine(cc)) {			// bad string			return pos;		}		if (cc === CC_BACK_SLASH) {			// we don't need to fully parse the escaped code point			// just skip over a potential new line			pos++;			if (pos === input.length) return pos;			pos++;		} else {			pos++;		}	}};const _isIdentifierStartCode = cc => {	return (		cc === CC_LOW_LINE ||		(cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||		(cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||		cc > 0x80	);};const _isDigit = cc => {	return cc >= CC_0 && cc <= CC_9;};const _startsIdentifier = (input, pos) => {	const cc = input.charCodeAt(pos);	if (cc === CC_HYPHEN_MINUS) {		if (pos === input.length) return false;		const cc = input.charCodeAt(pos + 1);		if (cc === CC_HYPHEN_MINUS) return true;		if (cc === CC_BACK_SLASH) {			const cc = input.charCodeAt(pos + 2);			return !_isNewLine(cc);		}		return _isIdentifierStartCode(cc);	}	if (cc === CC_BACK_SLASH) {		const cc = input.charCodeAt(pos + 1);		return !_isNewLine(cc);	}	return _isIdentifierStartCode(cc);};/** @type {CharHandler} */const consumeNumberSign = (input, pos, callbacks) => {	const start = pos;	pos++;	if (pos === input.length) return pos;	if (callbacks.isSelector(input, pos) && _startsIdentifier(input, pos)) {		pos = _consumeIdentifier(input, pos);		if (callbacks.id !== undefined) {			return callbacks.id(input, start, pos);		}	}	return pos;};/** @type {CharHandler} */const consumeMinus = (input, pos, callbacks) => {	const start = pos;	pos++;	if (pos === input.length) return pos;	const cc = input.charCodeAt(pos);	if (cc === CC_FULL_STOP || _isDigit(cc)) {		return consumeNumericToken(input, pos, callbacks);	} else if (cc === CC_HYPHEN_MINUS) {		pos++;		if (pos === input.length) return pos;		const cc = input.charCodeAt(pos);		if (cc === CC_GREATER_THAN_SIGN) {			return pos + 1;		} else {			pos = _consumeIdentifier(input, pos);			if (callbacks.identifier !== undefined) {				return callbacks.identifier(input, start, pos);			}		}	} else if (cc === CC_BACK_SLASH) {		if (pos + 1 === input.length) return pos;		const cc = input.charCodeAt(pos + 1);		if (_isNewLine(cc)) return pos;		pos = _consumeIdentifier(input, pos);		if (callbacks.identifier !== undefined) {			return callbacks.identifier(input, start, pos);		}	} else if (_isIdentifierStartCode(cc)) {		pos++;		pos = _consumeIdentifier(input, pos);		if (callbacks.identifier !== undefined) {			return callbacks.identifier(input, start, pos);		}	}	return pos;};/** @type {CharHandler} */const consumeDot = (input, pos, callbacks) => {	const start = pos;	pos++;	if (pos === input.length) return pos;	const cc = input.charCodeAt(pos);	if (_isDigit(cc)) return consumeNumericToken(input, pos - 2, callbacks);	if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))		return pos;	pos = _consumeIdentifier(input, pos);	if (callbacks.class !== undefined) return callbacks.class(input, start, pos);	return pos;};/** @type {CharHandler} */const consumeNumericToken = (input, pos, callbacks) => {	pos = _consumeNumber(input, pos);	if (pos === input.length) return pos;	if (_startsIdentifier(input, pos)) return _consumeIdentifier(input, pos);	const cc = input.charCodeAt(pos);	if (cc === CC_PERCENTAGE) return pos + 1;	return pos;};/** @type {CharHandler} */const consumeOtherIdentifier = (input, pos, callbacks) => {	const start = pos;	pos = _consumeIdentifier(input, pos);	if (		pos !== input.length &&		!callbacks.isSelector(input, pos) &&		input.charCodeAt(pos) === CC_LEFT_PARENTHESIS	) {		pos++;		if (callbacks.function !== undefined) {			return callbacks.function(input, start, pos);		}	} else {		if (callbacks.identifier !== undefined) {			return callbacks.identifier(input, start, pos);		}	}	return pos;};/** @type {CharHandler} */const consumePotentialUrl = (input, pos, callbacks) => {	const start = pos;	pos = _consumeIdentifier(input, pos);	if (pos === start + 3 && input.slice(start, pos + 1) === "url(") {		pos++;		let cc = input.charCodeAt(pos);		while (_isWhiteSpace(cc)) {			pos++;			if (pos === input.length) return pos;			cc = input.charCodeAt(pos);		}		if (cc === CC_QUOTATION_MARK || cc === CC_APOSTROPHE) {			pos++;			const contentStart = pos;			pos = _consumeString(input, pos, cc);			const contentEnd = pos - 1;			cc = input.charCodeAt(pos);			while (_isWhiteSpace(cc)) {				pos++;				if (pos === input.length) return pos;				cc = input.charCodeAt(pos);			}			if (cc !== CC_RIGHT_PARENTHESIS) return pos;			pos++;			if (callbacks.url !== undefined)				return callbacks.url(input, start, pos, contentStart, contentEnd);			return pos;		} else {			const contentStart = pos;			let contentEnd;			for (;;) {				if (cc === CC_BACK_SLASH) {					pos++;					if (pos === input.length) return pos;					pos++;				} else if (_isWhiteSpace(cc)) {					contentEnd = pos;					do {						pos++;						if (pos === input.length) return pos;						cc = input.charCodeAt(pos);					} while (_isWhiteSpace(cc));					if (cc !== CC_RIGHT_PARENTHESIS) return pos;					pos++;					if (callbacks.url !== undefined) {						return callbacks.url(input, start, pos, contentStart, contentEnd);					}					return pos;				} else if (cc === CC_RIGHT_PARENTHESIS) {					contentEnd = pos;					pos++;					if (callbacks.url !== undefined) {						return callbacks.url(input, start, pos, contentStart, contentEnd);					}					return pos;				} else if (cc === CC_LEFT_PARENTHESIS) {					return pos;				} else {					pos++;				}				if (pos === input.length) return pos;				cc = input.charCodeAt(pos);			}		}	} else {		if (callbacks.identifier !== undefined) {			return callbacks.identifier(input, start, pos);		}		return pos;	}};/** @type {CharHandler} */const consumePotentialPseudo = (input, pos, callbacks) => {	const start = pos;	pos++;	if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos))		return pos;	pos = _consumeIdentifier(input, pos);	let cc = input.charCodeAt(pos);	if (cc === CC_LEFT_PARENTHESIS) {		pos++;		if (callbacks.pseudoFunction !== undefined) {			return callbacks.pseudoFunction(input, start, pos);		}		return pos;	}	if (callbacks.pseudoClass !== undefined) {		return callbacks.pseudoClass(input, start, pos);	}	return pos;};/** @type {CharHandler} */const consumeLeftParenthesis = (input, pos, callbacks) => {	pos++;	if (callbacks.leftParenthesis !== undefined) {		return callbacks.leftParenthesis(input, pos - 1, pos);	}	return pos;};/** @type {CharHandler} */const consumeRightParenthesis = (input, pos, callbacks) => {	pos++;	if (callbacks.rightParenthesis !== undefined) {		return callbacks.rightParenthesis(input, pos - 1, pos);	}	return pos;};/** @type {CharHandler} */const consumeLeftCurlyBracket = (input, pos, callbacks) => {	pos++;	if (callbacks.leftCurlyBracket !== undefined) {		return callbacks.leftCurlyBracket(input, pos - 1, pos);	}	return pos;};/** @type {CharHandler} */const consumeRightCurlyBracket = (input, pos, callbacks) => {	pos++;	if (callbacks.rightCurlyBracket !== undefined) {		return callbacks.rightCurlyBracket(input, pos - 1, pos);	}	return pos;};/** @type {CharHandler} */const consumeSemicolon = (input, pos, callbacks) => {	pos++;	if (callbacks.semicolon !== undefined) {		return callbacks.semicolon(input, pos - 1, pos);	}	return pos;};/** @type {CharHandler} */const consumeComma = (input, pos, callbacks) => {	pos++;	if (callbacks.comma !== undefined) {		return callbacks.comma(input, pos - 1, pos);	}	return pos;};const _consumeIdentifier = (input, pos) => {	for (;;) {		const cc = input.charCodeAt(pos);		if (cc === CC_BACK_SLASH) {			pos++;			if (pos === input.length) return pos;			pos++;		} else if (			_isIdentifierStartCode(cc) ||			_isDigit(cc) ||			cc === CC_HYPHEN_MINUS		) {			pos++;		} else {			return pos;		}	}};const _consumeNumber = (input, pos) => {	pos++;	if (pos === input.length) return pos;	let cc = input.charCodeAt(pos);	while (_isDigit(cc)) {		pos++;		if (pos === input.length) return pos;		cc = input.charCodeAt(pos);	}	if (cc === CC_FULL_STOP && pos + 1 !== input.length) {		const next = input.charCodeAt(pos + 1);		if (_isDigit(next)) {			pos += 2;			cc = input.charCodeAt(pos);			while (_isDigit(cc)) {				pos++;				if (pos === input.length) return pos;				cc = input.charCodeAt(pos);			}		}	}	if (cc === CC_LOWER_E || cc === CC_UPPER_E) {		if (pos + 1 !== input.length) {			const next = input.charCodeAt(pos + 2);			if (_isDigit(next)) {				pos += 2;			} else if (				(next === CC_HYPHEN_MINUS || next === CC_PLUS_SIGN) &&				pos + 2 !== input.length			) {				const next = input.charCodeAt(pos + 2);				if (_isDigit(next)) {					pos += 3;				} else {					return pos;				}			} else {				return pos;			}		}	} else {		return pos;	}	cc = input.charCodeAt(pos);	while (_isDigit(cc)) {		pos++;		if (pos === input.length) return pos;		cc = input.charCodeAt(pos);	}	return pos;};/** @type {CharHandler} */const consumeLessThan = (input, pos, callbacks) => {	if (input.slice(pos + 1, pos + 4) === "!--") return pos + 4;	return pos + 1;};/** @type {CharHandler} */const consumeAt = (input, pos, callbacks) => {	const start = pos;	pos++;	if (pos === input.length) return pos;	if (_startsIdentifier(input, pos)) {		pos = _consumeIdentifier(input, pos);		if (callbacks.atKeyword !== undefined) {			pos = callbacks.atKeyword(input, start, pos);		}	}	return pos;};const CHAR_MAP = Array.from({ length: 0x80 }, (_, cc) => {	// https://drafts.csswg.org/css-syntax/#consume-token	switch (cc) {		case CC_LINE_FEED:		case CC_CARRIAGE_RETURN:		case CC_FORM_FEED:		case CC_TAB:		case CC_SPACE:			return consumeSpace;		case CC_QUOTATION_MARK:		case CC_APOSTROPHE:			return consumeString(cc);		case CC_NUMBER_SIGN:			return consumeNumberSign;		case CC_SLASH:			return consumePotentialComment;		// case CC_LEFT_SQUARE:		// case CC_RIGHT_SQUARE:		// case CC_COMMA:		// case CC_COLON:		// 	return consumeSingleCharToken;		case CC_COMMA:			return consumeComma;		case CC_SEMICOLON:			return consumeSemicolon;		case CC_LEFT_PARENTHESIS:			return consumeLeftParenthesis;		case CC_RIGHT_PARENTHESIS:			return consumeRightParenthesis;		case CC_LEFT_CURLY:			return consumeLeftCurlyBracket;		case CC_RIGHT_CURLY:			return consumeRightCurlyBracket;		case CC_COLON:			return consumePotentialPseudo;		case CC_PLUS_SIGN:			return consumeNumericToken;		case CC_FULL_STOP:			return consumeDot;		case CC_HYPHEN_MINUS:			return consumeMinus;		case CC_LESS_THAN_SIGN:			return consumeLessThan;		case CC_AT_SIGN:			return consumeAt;		case CC_LOWER_U:			return consumePotentialUrl;		case CC_LOW_LINE:			return consumeOtherIdentifier;		default:			if (_isDigit(cc)) return consumeNumericToken;			if (				(cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||				(cc >= CC_UPPER_A && cc <= CC_UPPER_Z)			) {				return consumeOtherIdentifier;			}			return consumeSingleCharToken;	}});/** * @param {string} input input css * @param {CssTokenCallbacks} callbacks callbacks * @returns {void} */module.exports = (input, callbacks) => {	let pos = 0;	while (pos < input.length) {		const cc = input.charCodeAt(pos);		if (cc < 0x80) {			pos = CHAR_MAP[cc](input, pos, callbacks);		} else {			pos++;		}	}};module.exports.eatComments = (input, pos) => {	loop: for (;;) {		const cc = input.charCodeAt(pos);		if (cc === CC_SLASH) {			if (pos === input.length) return pos;			let cc = input.charCodeAt(pos + 1);			if (cc !== CC_ASTERISK) return pos;			pos++;			for (;;) {				pos++;				if (pos === input.length) return pos;				cc = input.charCodeAt(pos);				while (cc === CC_ASTERISK) {					pos++;					if (pos === input.length) return pos;					cc = input.charCodeAt(pos);					if (cc === CC_SLASH) {						pos++;						continue loop;					}				}			}		}		return pos;	}};module.exports.eatWhitespaceAndComments = (input, pos) => {	loop: for (;;) {		const cc = input.charCodeAt(pos);		if (cc === CC_SLASH) {			if (pos === input.length) return pos;			let cc = input.charCodeAt(pos + 1);			if (cc !== CC_ASTERISK) return pos;			pos++;			for (;;) {				pos++;				if (pos === input.length) return pos;				cc = input.charCodeAt(pos);				while (cc === CC_ASTERISK) {					pos++;					if (pos === input.length) return pos;					cc = input.charCodeAt(pos);					if (cc === CC_SLASH) {						pos++;						continue loop;					}				}			}		} else if (_isWhiteSpace(cc)) {			pos++;			continue;		}		return pos;	}};
 |