Files
Outcut/content/behavior.js

292 lines
9.7 KiB
JavaScript

// Outlook Relook — Behavior Patches
// JS-based UX improvements, each gated by a setting key.
window.OutlookRelook = window.OutlookRelook || {};
window.OutlookRelook.Behavior = (function () {
'use strict';
var OR = window.OutlookRelook;
var currentSettings = {};
var cleanupFns = [];
// --- Auto-collapse ribbon on page load ---
function setupAutoCollapseRibbon() {
if (!currentSettings.autoCollapseRibbon) return;
// Wait for OWA to finish rendering, then collapse the ribbon
var timer = setTimeout(function () {
var elements = OR.resolveSelector('ribbon-collapse-button');
for (var i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('aria-expanded') === 'true') {
elements[i].click();
console.log('[Outlook Relook] Auto-collapsed ribbon');
}
}
}, 2000);
cleanupFns.push(function () { clearTimeout(timer); });
}
// --- Remember sidebar collapsed/expanded state ---
function setupRememberSidebar() {
if (!currentSettings.rememberSidebarState) return;
var savedState = localStorage.getItem('or-sidebar-collapsed');
if (savedState === 'true') {
var timer = setTimeout(function () {
var pane = OR.resolveSelector('folder-pane');
for (var i = 0; i < pane.length; i++) {
var toggle = pane[i].closest('[aria-expanded]') || pane[i].querySelector('[aria-expanded]');
if (toggle && toggle.getAttribute('aria-expanded') === 'true') {
toggle.click();
}
}
}, 2000);
cleanupFns.push(function () { clearTimeout(timer); });
}
// Watch for sidebar toggle changes
var sidebarObserver = new MutationObserver(function () {
var pane = OR.resolveSelector('folder-pane');
if (pane.length > 0) {
var isVisible = pane[0].offsetWidth > 50;
localStorage.setItem('or-sidebar-collapsed', String(!isVisible));
}
});
var timer2 = setTimeout(function () {
var pane = OR.resolveSelector('folder-pane');
if (pane.length > 0 && pane[0].parentElement) {
sidebarObserver.observe(pane[0].parentElement, { attributes: true, subtree: true });
}
}, 3000);
cleanupFns.push(function () {
clearTimeout(timer2);
sidebarObserver.disconnect();
});
}
// --- Suppress contact card hover popups ---
function setupSuppressContactHover() {
if (!currentSettings.suppressContactHover) return;
var handler = function (e) {
// Check if the hovered element or its ancestors trigger a contact card
var target = e.target.closest('[data-lpc-hover-target], [aria-haspopup="dialog"]');
if (target) {
e.stopPropagation();
e.preventDefault();
}
};
document.addEventListener('mouseenter', handler, true);
cleanupFns.push(function () { document.removeEventListener('mouseenter', handler, true); });
}
// --- Auto-advance to next email after delete ---
function setupAutoAdvance() {
if (!currentSettings.autoAdvanceAfterDelete) return;
var handler = function (e) {
// Detect delete key press
if (e.key === 'Delete' || e.key === 'Backspace') {
var selected = document.querySelector(
'[role="option"][aria-selected="true"], [role="listitem"][aria-selected="true"]'
);
if (selected) {
var next = selected.nextElementSibling;
if (next) {
// Wait for OWA to process the delete, then focus next
setTimeout(function () {
next.click();
next.focus();
}, 200);
}
}
}
};
document.addEventListener('keydown', handler, true);
cleanupFns.push(function () { document.removeEventListener('keydown', handler, true); });
}
// --- Auto-dismiss notification toasts ---
function setupAutoDismissToasts() {
if (currentSettings.autoDismissToasts === 'off') return;
var delay = parseInt(currentSettings.autoDismissToasts, 10) * 1000;
if (isNaN(delay) || delay <= 0) return;
var toastObserver = new MutationObserver(function (mutations) {
for (var m = 0; m < mutations.length; m++) {
var addedNodes = mutations[m].addedNodes;
for (var n = 0; n < addedNodes.length; n++) {
var node = addedNodes[n];
if (node.nodeType !== 1) continue;
var toasts = [];
if (node.matches && node.matches('[role="alert"], [role="status"][aria-live]')) {
toasts.push(node);
}
if (node.querySelectorAll) {
var found = node.querySelectorAll('[role="alert"], [role="status"][aria-live]');
for (var t = 0; t < found.length; t++) toasts.push(found[t]);
}
for (var i = 0; i < toasts.length; i++) {
(function (toast) {
setTimeout(function () {
// Try to find a dismiss button
var dismiss = toast.querySelector('[aria-label*="Close" i], [aria-label*="Dismiss" i]');
if (dismiss) {
dismiss.click();
} else {
toast.style.display = 'none';
}
}, delay);
})(toasts[i]);
}
}
}
});
toastObserver.observe(document.body, { childList: true, subtree: true });
cleanupFns.push(function () { toastObserver.disconnect(); });
}
// --- Reposition toast notifications ---
function setupToastPosition() {
if (currentSettings.toastPosition === 'bottom-left') return; // OWA default
var style = document.createElement('style');
style.id = 'or-toast-position';
style.textContent = [
'[role="alert"], [role="status"][aria-live] {',
' position: fixed !important;',
' top: 8px !important;',
' right: 8px !important;',
' bottom: auto !important;',
' left: auto !important;',
' z-index: 999999 !important;',
'}'
].join('\n');
document.head.appendChild(style);
cleanupFns.push(function () { style.remove(); });
}
// --- Sticky Reply/Forward bar ---
function setupStickyReplyBar() {
if (!currentSettings.stickyReplyBar) return;
var style = document.createElement('style');
style.id = 'or-sticky-reply';
style.textContent = [
'[aria-label*="Reply all" i][role="button"],',
'[aria-label*="Reply" i][role="button"],',
'[aria-label*="Forward" i][role="button"] {',
' position: sticky !important;',
' bottom: 0 !important;',
' z-index: 10 !important;',
' background-color: var(--or-bg-primary, #fff) !important;',
'}'
].join('\n');
document.head.appendChild(style);
cleanupFns.push(function () { style.remove(); });
}
// --- Auto-resize compose window ---
function setupAutoResizeCompose() {
if (!currentSettings.autoResizeCompose) return;
var composeObserver = new MutationObserver(function (mutations) {
for (var m = 0; m < mutations.length; m++) {
var addedNodes = mutations[m].addedNodes;
for (var n = 0; n < addedNodes.length; n++) {
var node = addedNodes[n];
if (node.nodeType !== 1) continue;
var composeWindows = [];
if (node.matches && node.matches('[aria-label*="compose" i][role="dialog"], [aria-label*="New message" i]')) {
composeWindows.push(node);
}
if (node.querySelectorAll) {
var found = node.querySelectorAll('[aria-label*="compose" i][role="dialog"], [aria-label*="New message" i]');
for (var i = 0; i < found.length; i++) composeWindows.push(found[i]);
}
for (var w = 0; w < composeWindows.length; w++) {
composeWindows[w].style.minHeight = '60vh';
composeWindows[w].style.height = '60vh';
console.log('[Outlook Relook] Auto-resized compose window');
}
}
}
});
composeObserver.observe(document.body, { childList: true, subtree: true });
cleanupFns.push(function () { composeObserver.disconnect(); });
}
// --- Throttle desktop notifications ---
function setupThrottleNotifications() {
if (!currentSettings.throttleNotifications) return;
var OrigNotification = window.Notification;
// Only patch if Notification API exists
if (!OrigNotification) return;
var lastNotificationTime = 0;
var MIN_INTERVAL = 30000; // 30 seconds between notifications
window.Notification = function (title, options) {
var now = Date.now();
if (now - lastNotificationTime < MIN_INTERVAL) {
console.log('[Outlook Relook] Throttled notification: "' + title + '"');
return {};
}
lastNotificationTime = now;
return new OrigNotification(title, options);
};
window.Notification.permission = OrigNotification.permission;
window.Notification.requestPermission = OrigNotification.requestPermission.bind(OrigNotification);
cleanupFns.push(function () {
window.Notification = OrigNotification;
});
}
// --- Public API ---
function start(settings) {
currentSettings = settings;
setupAutoCollapseRibbon();
setupRememberSidebar();
setupSuppressContactHover();
setupAutoAdvance();
setupAutoDismissToasts();
setupToastPosition();
setupStickyReplyBar();
setupAutoResizeCompose();
setupThrottleNotifications();
console.log('[Outlook Relook] Behavior patches applied');
}
function updateSettings(settings) {
// Tear down existing behaviors and re-apply
stop();
currentSettings = settings;
start(settings);
}
function stop() {
for (var i = 0; i < cleanupFns.length; i++) {
try { cleanupFns[i](); } catch (e) { /* ignore */ }
}
cleanupFns = [];
}
return { start: start, updateSettings: updateSettings, stop: stop };
})();