30 lines (28 loc) · 1013 B
 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};