| 165 | } |
| 166 | |
| 167 | function gobbleHex(str) { |
| 168 | const lower = str.toLowerCase(); |
| 169 | let hex = ""; |
| 170 | let spaceTerminated = false; |
| 171 | |
| 172 | // eslint-disable-next-line no-undefined |
| 173 | for (let i = 0; i < 6 && lower[i] !== undefined; i++) { |
| 174 | const code = lower.charCodeAt(i); |
| 175 | // check to see if we are dealing with a valid hex char [a-f|0-9] |
| 176 | const valid = (code >= 97 && code <= 102) || (code >= 48 && code <= 57); |
| 177 | // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point |
| 178 | spaceTerminated = code === 32; |
| 179 | |
| 180 | if (!valid) { |
| 181 | break; |
| 182 | } |
| 183 | |
| 184 | hex += lower[i]; |
| 185 | } |
| 186 | |
| 187 | if (hex.length === 0) { |
| 188 | // eslint-disable-next-line no-undefined |
| 189 | return undefined; |
| 190 | } |
| 191 | |
| 192 | const codePoint = parseInt(hex, 16); |
| 193 | |
| 194 | const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff; |
| 195 | // Add special case for |
| 196 | // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point" |
| 197 | // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point |
| 198 | if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) { |
| 199 | return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)]; |
| 200 | } |
| 201 | |
| 202 | return [ |
| 203 | String.fromCodePoint(codePoint), |
| 204 | hex.length + (spaceTerminated ? 1 : 0), |
| 205 | ]; |
| 206 | } |
| 207 | |
| 208 | const CONTAINS_ESCAPE = /\\/; |
| 209 | |