// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // @ts-check /// /// /// "use strict"; ((window) => { const { collectSequenceOfCodepoints, HTTP_WHITESPACE, HTTP_WHITESPACE_PREFIX_RE, HTTP_WHITESPACE_SUFFIX_RE, HTTP_QUOTED_STRING_TOKEN_POINT_RE, HTTP_TOKEN_CODE_POINT_RE, collectHttpQuotedString, } = window.__bootstrap.infra; /** * @typedef MimeType * @property {string} type * @property {string} subtype * @property {Map} parameters */ /** * @param {string} input * @returns {MimeType | null} */ function parseMimeType(input) { // 1. input = input.replaceAll(HTTP_WHITESPACE_PREFIX_RE, ""); input = input.replaceAll(HTTP_WHITESPACE_SUFFIX_RE, ""); // 2. let position = 0; const endOfInput = input.length; // 3. const res1 = collectSequenceOfCodepoints( input, position, (c) => c != "\u002F", ); const type = res1.result; position = res1.position; // 4. if (type === "" || !HTTP_TOKEN_CODE_POINT_RE.test(type)) return null; // 5. if (position >= endOfInput) return null; // 6. position++; // 7. const res2 = collectSequenceOfCodepoints( input, position, (c) => c != "\u003B", ); let subtype = res2.result; position = res2.position; // 8. subtype = subtype.replaceAll(HTTP_WHITESPACE_SUFFIX_RE, ""); // 9. if (subtype === "" || !HTTP_TOKEN_CODE_POINT_RE.test(subtype)) return null; // 10. const mimeType = { type: type.toLowerCase(), subtype: subtype.toLowerCase(), /** @type {Map} */ parameters: new Map(), }; // 11. while (position < endOfInput) { // 11.1. position++; // 11.2. const res1 = collectSequenceOfCodepoints( input, position, (c) => HTTP_WHITESPACE.includes(c), ); position = res1.position; // 11.3. const res2 = collectSequenceOfCodepoints( input, position, (c) => c !== "\u003B" && c !== "\u003D", ); let parameterName = res2.result; position = res2.position; // 11.4. parameterName = parameterName.toLowerCase(); // 11.5. if (position < endOfInput) { if (input[position] == "\u003B") continue; position++; } // 11.6. if (position >= endOfInput) break; // 11.7. let parameterValue = null; // 11.8. if (input[position] === "\u0022") { // 11.8.1. const res = collectHttpQuotedString(input, position, true); parameterValue = res.result; position = res.position; // 11.8.2. position++; } else { // 11.9. // 11.9.1. const res = collectSequenceOfCodepoints( input, position, (c) => c !== "\u003B", ); parameterValue = res.result; position = res.position; // 11.9.2. parameterValue = parameterValue.replaceAll( HTTP_WHITESPACE_SUFFIX_RE, "", ); // 11.9.3. if (parameterValue === "") continue; } // 11.10. if ( parameterName !== "" && HTTP_TOKEN_CODE_POINT_RE.test(parameterName) && HTTP_QUOTED_STRING_TOKEN_POINT_RE.test(parameterValue) && !mimeType.parameters.has(parameterName) ) { mimeType.parameters.set(parameterName, parameterValue); } } // 12. return mimeType; } /** * @param {MimeType} mimeType * @returns {string} */ function essence(mimeType) { return `${mimeType.type}/${mimeType.subtype}`; } /** * @param {MimeType} mimeType * @returns {string} */ function serializeMimeType(mimeType) { let serialization = essence(mimeType); for (const param of mimeType.parameters) { serialization += `;${param[0]}=`; let value = param[1]; if (!HTTP_TOKEN_CODE_POINT_RE.test(value)) { value = value.replaceAll("\\", "\\\\"); value = value.replaceAll('"', '\\"'); value = `"${value}"`; } serialization += value; } return serialization; } window.__bootstrap.mimesniff = { parseMimeType, essence, serializeMimeType }; })(this);