/* global URL, TextDecoder, TextEncoder, btoa, atob */ const EVENT_HANDLER_ATTRIBUTES = [ "onafterprint", "onbeforeprint", "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage", "onmessageerror", "onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate", "onrejectionhandled", "onstorage", "onunhandledrejection", "onunload", "ongamepadconnected", "ongamepaddisconnected", "onabort", "onblur", "onfocus", "oncancel", "onauxclick", "onbeforeinput", "onbeforetoggle", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontentvisibilityautostatechange", "oncontextlost", "oncontextmenu", "oncontextrestored", "oncopy", "oncuechange", "oncut", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onformdata", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmousedown", "onmouseenter", "onmouseleave", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onwheel", "onpaste", "onpause", "onplay", "onplaying", "onprogress", "onratechange", "onreset", "onresize", "onscroll", "onscrollend", "onsecuritypolicyviolation", "onseeked", "onseeking", "onselect", "onslotchange", "onstalled", "onsubmit", "onsuspend", "ontimeupdate", "onvolumechange", "onwaiting", "onselectstart", "onselectionchange", "ontoggle", "onpointercancel", "onpointerdown", "onpointerup", "onpointermove", "onpointerout", "onpointerover", "onpointerenter", "onpointerleave", "ongotpointercapture", "onlostpointercapture", "onanimationcancel", "onanimationend", "onanimationiteration", "onanimationstart", "ontransitioncancel", "ontransitionend", "ontransitionrun", "ontransitionstart", "onerror", "onfullscreenchange", "onfullscreenerror" ]; export { EVENT_HANDLER_ATTRIBUTES, decodeQuotedPrintable, decodeBinary, decodeMimeHeader, parseDOM, decodeBase64, decodeString, encodeString, getCharset, replaceCharset, isDocument, isStylesheet, isText, isMultipartAlternative, getBoundary, indexOf, startsWithBoundary, isLineFeed, endsWithCRLF, endsWithLF, getResourceURI, resolvePath }; function decodeQuotedPrintable(array) { const result = []; for (let i = 0; i < array.length; i++) { if (array[i] === 0x3D) { if (isHex(array[i + 1]) && isHex(array[i + 2])) { const hex = parseInt(String.fromCharCode(array[i + 1], array[i + 2]), 16); result.push(hex); i += 2; } else { result.push(array[i]); } } else { result.push(array[i]); } } return new Uint8Array(result); function isHex(value) { return value >= 0x30 && value <= 0x39 || value >= 0x41 && value <= 0x46; } } function decodeBinary(array) { let data = ""; for (let indexData = 0; indexData < array.length; indexData++) { data += String.fromCharCode(array[indexData]); } return btoa(data); } function decodeBase64(value, charset) { const decodedData = new Uint8Array(atob(value).split("").map(char => char.charCodeAt(0))); return new TextDecoder(charset).decode(decodedData); } function decodeMimeHeader(encodedSubject) { if (encodedSubject && encodedSubject.startsWith("=?") && encodedSubject.endsWith("?=")) { const encodedSubjectParts = []; let index = 0; while (index < encodedSubject.length) { const start = encodedSubject.indexOf("=?", index); if (start === -1) { break; } const endCharset = encodedSubject.indexOf("?", start + 2); if (endCharset === -1) { break; } const charset = encodedSubject.substring(start + 2, endCharset); const endEncoding = encodedSubject.indexOf("?", endCharset + 1); if (endEncoding === -1) { break; } const encoding = encodedSubject.substring(endCharset + 1, endEncoding); const endValue = encodedSubject.indexOf("?=", endEncoding + 1); if (endValue === -1) { break; } const value = encodedSubject.substring(endEncoding + 1, endValue); index = endValue + 2; if (encoding === "Q") { encodedSubjectParts.push(new TextDecoder(charset).decode(decodeQuotedPrintable(new TextEncoder().encode(value)))); } else if (encoding === "B") { encodedSubjectParts.push(decodeBase64(value, charset)); } } encodedSubject = encodedSubjectParts.join(""); } return encodedSubject || ""; } function parseDOM(asset, contentType = "text/html", DOMParser = globalThis.DOMParser) { let document; try { document = new DOMParser().parseFromString(asset, contentType); // eslint-disable-next-line no-unused-vars } catch (_) { document = new DOMParser().parseFromString(asset, "text/html"); } return { document, serialize() { let result = ""; if (this.document.doctype) { result += serializeDocType(this.document.doctype) + "\n"; } result += this.document.documentElement.outerHTML; return result; } }; } function serializeDocType(doctype) { return ``; } function decodeString(array, charset) { return new TextDecoder(charset).decode(array); } function encodeString(string, charset) { return new TextEncoder(charset).encode(string); } function getCharset(contentType) { const charsetMatch = contentType.match(/charset=([^;]+)/); if (charsetMatch) { return removeQuotes(charsetMatch[1]).toLowerCase(); } } function removeQuotes(value) { return value.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1").trim(); } function replaceCharset(contentType, charset) { return contentType.replace(/charset=([^;]+)/, `charset=${charset}`); } function isDocument(contentType) { return contentType.startsWith("text/html") || contentType.startsWith("application/xhtml+xml"); } function isStylesheet(contentType) { return contentType.startsWith("text/css"); } function isText(contentType) { return contentType.startsWith("text/"); } function isMultipartAlternative(contentType) { return contentType.startsWith("multipart/alternative"); } function getBoundary(contentType) { const contentTypeParams = contentType.split(";"); contentTypeParams.shift(); const boundaryParam = contentTypeParams.find(param => param.startsWith("boundary=")); if (boundaryParam) { return removeQuotes(boundaryParam.substring(9)); } } function indexOf(array, string) { const stringBytes = new TextEncoder().encode(string); for (let i = 0; i < array.length; i++) { if (array[i] === stringBytes[0]) { let match = true; for (let j = 1; j < stringBytes.length; j++) { if (array[i + j] !== stringBytes[j]) { match = false; break; } } if (match) { // return index return i; } } } return -1; } function isLineFeed(array) { return array.length == 2 ? array[0] == 0x0D && array[1] == 0x0A : array.length == 1 ? array[0] == 0x0A : false; } function endsWithCRLF(array) { return array.length >= 2 ? array[array.length - 2] == 0x0D && array[array.length - 1] == 0x0A : array.length >= 1 ? array[array.length - 1] == 0x0D : false; } function endsWithLF(array) { return array.length >= 1 ? array[array.length - 1] == 0x0A : false; } function startsWithBoundary(array) { return array.length >= 2 ? array[0] == 0x2D && array[1] == 0x2D : false; } function getResourceURI({ contentType, transferEncoding, data }) { return `data:${contentType};${"base64"},${transferEncoding === "base64" ? data : btoa(unescape(encodeURIComponent(data)))}`; } function resolvePath(path, base) { if (base && !path.startsWith("data:")) { try { return new URL(path, base).href; // eslint-disable-next-line no-unused-vars } catch (_) { if (path.startsWith("//")) { const protocol = base.match(/^[^:]+/); if (protocol) { return `${protocol[0]}:${path}`; } else { return path; } } else { return path; } } } else { return path; } }