OutlookWebInboxCount/content.js
Giorgio Gilestro 922d520f02 Initial commit: Outlook Web Inbox Count extension v1.4
Chrome/Firefox extension that displays the total inbox item count next
to the Inbox label in Outlook Web. Includes build+publish automation
via the Chrome Web Store API (see PUBLISHING.md).
2026-04-09 07:56:37 +01:00

323 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Outlook Web Inbox Count Extension v1.4
// Changelog v1.4:
// - Added outlook.cloud.microsoft to manifest matches (Microsoft migrated Outlook Web to new domain)
// Changelog v1.3:
// - Fixed Firefox compatibility by adding browser_specific_settings to manifest
// - Fixed bug where findTotalItems() wasn't returning values due to forEach callbacks
// - Changed forEach loops to for loops to enable proper return statements
// - Extended retry mechanism with more attempts (1s, 2s, 3s, 5s, 8s, 10s, 15s, 20s)
// - Added re-logging of environment info on each retry to track page load progress
// Changelog v1.2:
// - Added comprehensive debugging information to help diagnose user issues
// - Added URL detection and page structure analysis
// - Enhanced logging for DOM element discovery
// - Added language/locale detection
// Changelog v1.1:
// - Fixed browser hanging issue by adding throttling mechanism
// - Improved MutationObserver to be more selective and prevent infinite loops
// - Enhanced DOM selectors with multiple fallbacks for different Outlook versions
// - Reduced polling frequency to improve performance
// - Added better pattern matching for inbox count extraction
console.log("Outlook Inbox Count Extension v1.4: Script started");
// Enhanced debugging information
function logEnvironmentInfo() {
console.log("=== OUTLOOK INBOX EXTENSION DEBUG INFO ===");
console.log("URL:", window.location.href);
console.log("Domain:", window.location.hostname);
console.log("User Agent:", navigator.userAgent);
console.log("Language:", navigator.language);
console.log("Document ready state:", document.readyState);
console.log("Page title:", document.title);
// Check for New Outlook indicators
const isNewOutlook = document.querySelector('[data-app-section="NewMailModule"]') ||
document.querySelector('div[role="main"][data-app-section]') ||
document.body.classList.contains('new-outlook') ||
window.location.href.includes('mail.office365.com');
console.log("Detected New Outlook interface:", isNewOutlook);
// Log all elements that might be inbox-related
console.log("=== DOM ANALYSIS ===");
const potentialInboxElements = document.querySelectorAll('[data-folder-name], [title*="Inbox"], [aria-label*="Inbox"], button[title*="nbox"], div[title*="nbox"]');
console.log("Found", potentialInboxElements.length, "potential inbox elements:");
potentialInboxElements.forEach((el, i) => {
console.log(`Element ${i}:`, {
tagName: el.tagName,
className: el.className,
title: el.getAttribute('title'),
dataFolderName: el.getAttribute('data-folder-name'),
ariaLabel: el.getAttribute('aria-label'),
textContent: el.textContent?.substring(0, 100)
});
});
console.log("=== END DOM ANALYSIS ===");
}
let isProcessing = false;
let lastProcessTime = 0;
const THROTTLE_DELAY = 1000; // 1 second throttle
function findInboxElement() {
console.log("=== SEARCHING FOR INBOX ELEMENT ===");
// Try multiple selectors to find inbox element
const selectors = [
'div[title^="Inbox -"][data-folder-name="inbox"]',
'div[data-folder-name="inbox"]',
'div[title*="Inbox"]',
'button[title^="Inbox -"]',
'button[data-folder-name="inbox"]',
// Additional selectors for different Outlook versions
'button[aria-label*="Inbox"]',
'div[aria-label*="Inbox"]',
'[data-folder-id="inbox"]',
'[data-automation-id*="inbox"]',
'span[title*="Inbox"]'
];
console.log("Trying", selectors.length, "different selectors...");
for (let i = 0; i < selectors.length; i++) {
const selector = selectors[i];
console.log(`Trying selector ${i + 1}/${selectors.length}:`, selector);
const elements = document.querySelectorAll(selector);
console.log(`Found ${elements.length} elements with this selector`);
if (elements.length > 0) {
elements.forEach((el, idx) => {
console.log(` Element ${idx}:`, {
tagName: el.tagName,
className: el.className,
title: el.getAttribute('title'),
ariaLabel: el.getAttribute('aria-label'),
dataFolderName: el.getAttribute('data-folder-name'),
textContent: el.textContent?.substring(0, 50) + '...'
});
});
const inboxDiv = elements[0]; // Use the first match
console.log("✓ Using first element as inbox:", inboxDiv);
const span = inboxDiv.querySelector('span:not(.added-count)');
if (span) {
console.log("✓ Found inner span element:", span);
return span;
}
// If no span found, try to use the element itself
console.log(" No inner span found, using element itself");
return inboxDiv;
}
}
console.log("❌ Inbox element not found with any selector");
console.log("=== END INBOX ELEMENT SEARCH ===");
return null;
}
function findTotalItems() {
console.log("=== SEARCHING FOR TOTAL ITEMS COUNT ===");
// Try multiple selectors and patterns
const selectors = [
'div[title^="Inbox -"][data-folder-name="inbox"]',
'div[data-folder-name="inbox"]',
'div[title*="Inbox"]',
'button[title^="Inbox -"]',
'button[data-folder-name="inbox"]',
'button[aria-label*="Inbox"]',
'div[aria-label*="Inbox"]',
'[data-folder-id="inbox"]'
];
const patterns = [
/Inbox - (\d+) items/i,
/Inbox \((\d+)\)/i,
/(\d+) items/i,
/\((\d+)\)/i,
// Additional patterns for different languages/formats
/Inbox.*?(\d+)/i,
/(\d+).*?unread/i,
/(\d+).*?message/i,
/(\d+).*?email/i
];
// Also check document title which often contains count
console.log("Checking document title:", document.title);
for (const pattern of patterns) {
const match = document.title.match(pattern);
if (match) {
const totalItems = match[1];
console.log(`✓ Total items found in document title: ${totalItems}`);
return totalItems;
}
}
console.log("Checking DOM elements...");
for (const selector of selectors) {
const elements = document.querySelectorAll(selector);
console.log(`Checking selector: ${selector} (${elements.length} elements)`);
for (let idx = 0; idx < elements.length; idx++) {
const inboxDiv = elements[idx];
const sources = [
{ name: 'title', value: inboxDiv.getAttribute('title') },
{ name: 'aria-label', value: inboxDiv.getAttribute('aria-label') },
{ name: 'textContent', value: inboxDiv.textContent }
];
for (const source of sources) {
if (!source.value) continue;
console.log(` Element ${idx} ${source.name}:`, source.value.substring(0, 100));
for (const pattern of patterns) {
const match = source.value.match(pattern);
if (match) {
const totalItems = match[1];
console.log(`✓ Total items found in ${source.name}: ${totalItems}`);
return totalItems;
}
}
}
}
}
console.log("❌ Total items count not found in any source");
console.log("=== END TOTAL ITEMS SEARCH ===");
return null;
}
function addInboxCount() {
const now = Date.now();
// Throttle execution to prevent excessive calls
if (isProcessing || (now - lastProcessTime) < THROTTLE_DELAY) {
return;
}
isProcessing = true;
lastProcessTime = now;
console.log("addInboxCount function called");
const inboxElement = findInboxElement();
if (!inboxElement) {
console.log("Inbox element not found, will try again later");
isProcessing = false;
return;
}
const totalItems = findTotalItems();
console.log("Total items:", totalItems);
// Check if we've already added the count
let countSpan = inboxElement.querySelector('.added-count');
if (!countSpan) {
// Create a new span element for the count
countSpan = document.createElement('span');
countSpan.style.marginLeft = '5px';
countSpan.style.fontWeight = 'bold';
countSpan.className = 'added-count';
// Add the count next to the inbox label
inboxElement.appendChild(countSpan);
console.log("Count span added to inbox label");
}
// Update the count
if (totalItems) {
countSpan.textContent = ` (${totalItems})`;
countSpan.style.color = ''; // Reset to default color
} else {
countSpan.textContent = ' (-)';
countSpan.style.color = '#888'; // Grey color
}
console.log("Count span updated");
isProcessing = false;
}
function init() {
console.log("=== INITIALIZING OUTLOOK INBOX COUNT EXTENSION ===");
// Log environment information first
logEnvironmentInfo();
console.log("Starting initial inbox count attempt...");
addInboxCount();
// Try to add the inbox count multiple times during startup with extended delays
// Firefox may need more time for Outlook's dynamic content to load
console.log("Scheduling additional attempts at 1s, 2s, 3s, 5s, 8s, 10s, 15s, and 20s...");
const retryTimes = [1000, 2000, 3000, 5000, 8000, 10000, 15000, 20000];
retryTimes.forEach(delay => {
setTimeout(() => {
console.log(`=== ${delay/1000}-SECOND RETRY ===`);
logEnvironmentInfo(); // Re-log to see if page has loaded
addInboxCount();
}, delay);
});
// Then, check every 15 seconds (slightly increased for debugging)
setInterval(() => {
console.log("=== PERIODIC CHECK ===");
addInboxCount();
}, 15000);
console.log("=== INITIALIZATION COMPLETE ===");
}
// Use a MutationObserver to watch for changes in the DOM
const observer = new MutationObserver((mutations) => {
let shouldUpdate = false;
for (let mutation of mutations) {
// Only update if attributes changed on elements that might be inbox-related
if (mutation.type === 'attributes' && mutation.attributeName === 'title') {
const target = mutation.target;
if (target.hasAttribute && target.hasAttribute('data-folder-name')) {
shouldUpdate = true;
break;
}
}
// Or if new inbox elements were added
else if (mutation.type === 'childList') {
const nodes = [...(mutation.addedNodes || [])];
if (nodes.some(node => node.nodeType === 1 &&
(node.hasAttribute?.('data-folder-name') ||
node.querySelector?.('[data-folder-name="inbox"]')))) {
shouldUpdate = true;
break;
}
}
}
if (shouldUpdate) {
addInboxCount();
}
});
// Start observing with more targeted configuration
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['title', 'data-folder-name']
});
// Run the init function when the DOM is fully loaded
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
// Listen for messages from the background script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "URL_CHANGED") {
console.log("URL changed, running inbox count function");
addInboxCount();
}
});