Removes Outlook Web ad/upgrade cards consistently.
// ==UserScript==
// @name Outlook Web: Remove Upgrade/Ad Card
// @namespace berg.tool
// @license MIT
// @version 1.0
// @description Removes Outlook Web ad/upgrade cards consistently.
// @author @BergNetworks
// @match https://outlook.live.com/*
// @match https://outlook.office.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
// You can thank AI for syntax checks and code cleanup. I suck at this.
(function () {
"use strict";
const TEXT_MARKERS = [
/Upgrade Your Account/i,
/Get the latest premium Outlook features/i,
/Microsoft Outlook/i,
];
const BADGE_TEXT = /^Ad$/i;
const AVATAR_IMG_HINT = /res\.public\.onecdn\.static\.microsoft\/owamail\/.*\/ads-olk-icon\.png/i;
const ROOT_SELECTOR = '[data-animatable="true"]';
const MAX_ASCEND_STEPS = 6;
const HIDE_INSTEAD = true;
const isElement = (n) => n && n.nodeType === 1;
const getTxt = (el) => (el && el.innerText ? el.innerText.trim() : "");
const findAncestorCard = (start) => {
let el = start;
let steps = 0;
while (isElement(el) && steps < MAX_ASCEND_STEPS) {
if (el.matches(ROOT_SELECTOR)) return el;
el = el.parentElement;
steps++;
}
return start.closest("div, section, article") || start;
};
const kill = (node) => {
const target = findAncestorCard(node);
if (!isElement(target)) return;
if (HIDE_INSTEAD) {
target.setAttribute("data-outlook-ad-removed", "1");
target.style.setProperty("display", "none", "important");
target.style.setProperty("visibility", "hidden", "important");
} else {
target.remove();
}
};
const looksLikeOutlookAdCard = (container) => {
if (!isElement(container)) return false;
const text = getTxt(container);
const hasAllText = TEXT_MARKERS.every((re) => re.test(text));
if (!hasAllText) return false;
const adBadge = container.querySelector("div,span,i");
const hasBadge = !![...container.querySelectorAll("div,span,i")].find(
(n) => BADGE_TEXT.test(n.textContent || "")
);
const hasOlkIcon = !![...container.querySelectorAll("img")].find((img) =>
AVATAR_IMG_HINT.test(img.getAttribute("src") || "")
);
return hasBadge && hasOlkIcon;
};
const scan = (root) => {
if (!root) return;
const candidates = [];
if (isElement(root) && looksLikeOutlookAdCard(root)) candidates.push(root);
const tw = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
let node;
while ((node = tw.nextNode())) {
if (looksLikeOutlookAdCard(node)) candidates.push(node);
}
candidates.forEach(kill);
};
const observe = () => {
const mo = new MutationObserver((muts) => {
for (const m of muts) {
if (m.type === "childList") {
m.addedNodes.forEach((n) => isElement(n) && scan(n));
} else if (m.type === "attributes") {
isElement(m.target) && scan(m.target);
}
}
});
mo.observe(document.documentElement, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["class", "style", "aria-hidden", "role", "id"],
});
};
const start = () => {
scan(document);
observe();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", start, { once: true });
} else {
start();
}
})();