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).
323 lines
11 KiB
JavaScript
323 lines
11 KiB
JavaScript
// 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();
|
||
}
|
||
});
|