1import { $$ } from "./dom";
2
3// Decode a Cloudflare-obfuscated email: each byte is XOR'd with the leading
4// key byte.
5const decode = (hex: string): string => {
6 let out = "";
7 const key = parseInt(hex.slice(0, 2), 16);
8 for (let i = 2; i < hex.length; i += 2) {
9 out += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16) ^ key);
10 }
11 return out;
12};
13
14/**
15 * Decode Cloudflare-obfuscated emails. Cloudflare's own decoder runs only on a
16 * full page load, so addresses injected by the router are decoded here.
17 */
18export const decodeCloudflareEmails = (root: ParentNode = document): void => {
19 $$<HTMLElement>("[data-cfemail]", root).forEach((el) => {
20 const hex = el.dataset.cfemail;
21 if (!hex) return;
22 const email = decode(hex);
23 el.textContent = email;
24 el.classList.remove("__cf_email__");
25 const link =
26 el.closest<HTMLAnchorElement>('a[href*="email-protection"]') ||
27 (el instanceof HTMLAnchorElement ? el : null);
28 if (link) link.href = "mailto:" + email;
29 });
30};