| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} CssTokenCallbacks | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  |  * @property {function(string, number): boolean} isSelector | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  |  * @property {function(string, number, number, number, number): number=} url | 
					
						
							| 
									
										
										
										
											2021-12-01 20:27:00 +08:00
										 |  |  |  * @property {function(string, number, number): number=} string | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  |  * @property {function(string, number, number): number=} leftParenthesis | 
					
						
							|  |  |  |  * @property {function(string, number, number): number=} rightParenthesis | 
					
						
							|  |  |  |  * @property {function(string, number, number): number=} pseudoFunction | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  |  * @property {function(string, number, number): number=} function | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  |  * @property {function(string, number, number): number=} pseudoClass | 
					
						
							| 
									
										
										
										
											2021-12-01 20:27:00 +08:00
										 |  |  |  * @property {function(string, number, number): number=} atKeyword | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  |  * @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 (;;) { | 
					
						
							| 
									
										
										
										
											2021-12-14 23:02:26 +08:00
										 |  |  | 		pos++; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 		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) => { | 
					
						
							| 
									
										
										
										
											2021-12-01 20:27:00 +08:00
										 |  |  | 	const start = pos; | 
					
						
							|  |  |  | 	pos = _consumeString(input, pos, end); | 
					
						
							|  |  |  | 	if (callbacks.string !== undefined) { | 
					
						
							|  |  |  | 		pos = callbacks.string(input, start, pos); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return pos; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  | 	if (callbacks.isSelector(input, pos) && _startsIdentifier(input, pos)) { | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  | 	if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos)) | 
					
						
							|  |  |  | 		return pos; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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) { | 
					
						
							| 
									
										
										
										
											2021-12-03 23:23:09 +08:00
										 |  |  | 					contentEnd = pos; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 					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++; | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  | 	if (!callbacks.isSelector(input, pos) || !_startsIdentifier(input, pos)) | 
					
						
							|  |  |  | 		return pos; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 	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) => { | 
					
						
							| 
									
										
										
										
											2021-12-01 20:27:00 +08:00
										 |  |  | 	const start = pos; | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 	pos++; | 
					
						
							|  |  |  | 	if (pos === input.length) return pos; | 
					
						
							|  |  |  | 	if (_startsIdentifier(input, pos)) { | 
					
						
							| 
									
										
										
										
											2021-12-01 20:27:00 +08:00
										 |  |  | 		pos = _consumeIdentifier(input, pos); | 
					
						
							|  |  |  | 		if (callbacks.atKeyword !== undefined) { | 
					
						
							|  |  |  | 			pos = callbacks.atKeyword(input, start, pos); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-07 15:08:58 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-12-14 23:02:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |