// ==UserScript==
// @name DC Notif'
// @namespace http://tampermonkey.net/
// @version 1.6.6
// @description Notifications traitées dans une interface
// @author Laïn
// @match https://www.dreadcast.net/Main*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const CHECK_URL = 'https://www.dreadcast.net/Check';
const MAX_HISTORY_ENTRIES = 50;
const SCRIPT_Z_INDEX_BASE = 100001;
const BELL_MARGIN_TOP = 10;
const MAX_RETRIES = 10;
const RETRY_DELAY = 750;
const SHAKE_ANIMATION_DURATION = 800;
const PERIODIC_CLEANUP_INTERVAL = 15000; // 15 seconds
const DEFAULT_NOTIFY_SOUND_URL = 'none';
const DEFAULT_NOTIFY_VOLUME = 0.6;
const DEFAULT_BELL_SIZE_EM = 2.75;
const DEFAULT_HIDE_ORIGINAL_NOTIFS = true;
const LS_BELL_SIZE = 'dcNotify_bellSize';
const LS_BELL_POS_TOP = 'dcNotify_bellPosTop';
const LS_BELL_POS_LEFT = 'dcNotify_bellPosLeft';
const LS_NOTIFY_SOUND_URL = 'dcNotify_notifySoundUrl';
const LS_NOTIFY_SOUND_VOLUME = 'dcNotify_notifySoundVolume';
const LS_HISTORY_PANEL_POS_TOP = 'dcNotify_historyPanelPosTop';
const LS_HISTORY_PANEL_POS_LEFT = 'dcNotify_historyPanelPosLeft';
const LS_SETTINGS_PANEL_POS_TOP = 'dcNotify_settingsPanelPosTop';
const LS_SETTINGS_PANEL_POS_LEFT = 'dcNotify_settingsPanelPosLeft';
const LS_HIDE_ORIGINAL_NOTIFS = 'dcNotify_hideOriginalNotifs';
const LS_NOTIFICATION_CATEGORIES = 'dcNotify_notificationCategories';
const LS_CATEGORY_STATES = 'dcNotify_categoryStates';
const LS_FILTERS_COLLAPSED = 'dcNotify_filtersCollapsed';
const BODY_CLASS_HIDE_ORIGINAL = 'dc-notify-hide-original';
const NOTIF_PREFIX_ALERTE = 'alerte-';
const NOTIF_PREFIX_VENTE = 'objet-vendu';
const GENERIC_KEY_ALERTE = 'Alerte';
const GENERIC_KEY_VENTE = 'Objet-vendu';
let evilBoxHistory = [];
let notificationCategories;
let categoryStates = {};
let notifyAlertAudio = null;
let historyPanel = null;
let settingsPanel = null;
let unreadCount = 0;
let counterElement = null;
let addBellIconTimeout = null;
let addBellIconRetryCount = 0;
let isDraggingPanel = false;
let initialMouseX = 0, initialMouseY = 0, initialPanelTop = 0, initialPanelLeft = 0;
let bellElement = null;
let isBellMoveModeActive = false;
let isDraggingBell = false;
let initialBellDragX = 0, initialBellDragY = 0, initialBellTop = 0, initialBellLeft = 0;
let currentBellSize = parseFloat(localStorage.getItem(LS_BELL_SIZE) || DEFAULT_BELL_SIZE_EM);
let currentNotifySoundUrl = localStorage.getItem(LS_NOTIFY_SOUND_URL) || DEFAULT_NOTIFY_SOUND_URL;
let currentNotifyVolume = parseFloat(localStorage.getItem(LS_NOTIFY_SOUND_VOLUME) || DEFAULT_NOTIFY_VOLUME);
let hideOriginalNotifications = localStorage.getItem(LS_HIDE_ORIGINAL_NOTIFS) === null ? DEFAULT_HIDE_ORIGINAL_NOTIFS : localStorage.getItem(LS_HIDE_ORIGINAL_NOTIFS) === 'true';
let filtersCollapsed = localStorage.getItem(LS_FILTERS_COLLAPSED) === 'true';
let savedBellPos = {
top: localStorage.getItem(LS_BELL_POS_TOP),
left: localStorage.getItem(LS_BELL_POS_LEFT)
};
let savedHistoryPanelPos = {
top: localStorage.getItem(LS_HISTORY_PANEL_POS_TOP),
left: localStorage.getItem(LS_HISTORY_PANEL_POS_LEFT)
};
let savedSettingsPanelPos = {
top: localStorage.getItem(LS_SETTINGS_PANEL_POS_TOP),
left: localStorage.getItem(LS_SETTINGS_PANEL_POS_LEFT)
};
let evilBoxDismissQueue = new Set();
let cleanupIntervalId = null;
function getEffectiveCategoryKey(category) {
const lowerCategory = category.toLowerCase();
if (lowerCategory.startsWith(NOTIF_PREFIX_VENTE)) {
return GENERIC_KEY_VENTE;
}
if (lowerCategory.startsWith(NOTIF_PREFIX_ALERTE)) {
return GENERIC_KEY_ALERTE;
}
return category;
}
function loadNotificationCategories() {
const storedCategories = localStorage.getItem(LS_NOTIFICATION_CATEGORIES);
if (storedCategories) {
try {
const parsedCategories = JSON.parse(storedCategories);
notificationCategories = new Set(parsedCategories.map(cat => getEffectiveCategoryKey(cat)));
} catch (e) {
notificationCategories = new Set();
}
} else {
notificationCategories = new Set();
}
}
function saveNotificationCategories() {
localStorage.setItem(LS_NOTIFICATION_CATEGORIES, JSON.stringify(Array.from(notificationCategories)));
}
function loadCategoryStates() {
const storedStates = localStorage.getItem(LS_CATEGORY_STATES);
let loadedStates;
try {
loadedStates = storedStates ? JSON.parse(storedStates) : {};
} catch (e) {
loadedStates = {};
}
categoryStates = {};
let changed = false;
notificationCategories.forEach(cat => {
categoryStates[cat] = 'inactive';
if (loadedStates[cat] && ['active', 'excluded', 'inactive'].includes(loadedStates[cat])) {
categoryStates[cat] = loadedStates[cat];
}
});
Object.keys(loadedStates).forEach(loadedCatKey => {
const effectiveKey = getEffectiveCategoryKey(loadedCatKey);
if (notificationCategories.has(effectiveKey)) {
if (!categoryStates[effectiveKey] || categoryStates[effectiveKey] === 'inactive') {
if (loadedStates[loadedCatKey] && ['active', 'excluded'].includes(loadedStates[loadedCatKey])) {
categoryStates[effectiveKey] = loadedStates[loadedCatKey];
changed = true;
}
}
} else {
if (loadedStates[loadedCatKey] && effectiveKey && !notificationCategories.has(effectiveKey)) {
notificationCategories.add(effectiveKey);
categoryStates[effectiveKey] = loadedStates[loadedCatKey];
changed = true;
}
}
});
notificationCategories.forEach(cat => {
if (!(cat in categoryStates)) {
categoryStates[cat] = 'inactive';
changed = true;
}
});
Object.keys(categoryStates).forEach(cat => {
const effectiveCat = getEffectiveCategoryKey(cat);
if (!notificationCategories.has(effectiveCat) && cat !== effectiveCat) {
delete categoryStates[cat];
changed = true;
} else if (!notificationCategories.has(cat) && cat === effectiveCat) {
delete categoryStates[cat];
changed = true;
}
});
if (changed) {
saveCategoryStates();
saveNotificationCategories();
}
}
function saveCategoryStates() {
localStorage.setItem(LS_CATEGORY_STATES, JSON.stringify(categoryStates));
}
loadNotificationCategories();
loadCategoryStates();
function playNotificationSound() {
if (historyPanel && historyPanel.parentNode) {
return;
}
testSound(currentNotifySoundUrl, currentNotifyVolume);
}
function testSound(url, volume) {
if (!url || url === DEFAULT_NOTIFY_SOUND_URL || !(url.startsWith('http://') || url.startsWith('https://'))) {
return;
}
volume = parseFloat(volume);
if (isNaN(volume) || volume < 0 || volume > 1) {
volume = DEFAULT_NOTIFY_VOLUME;
}
try {
const testAudio = new Audio(url);
testAudio.volume = volume;
testAudio.play().catch(error => {});
} catch (e) {}
}
function updateUnreadCounterDisplay() {
if (!counterElement) return;
if (unreadCount > 0) {
counterElement.textContent = unreadCount;
counterElement.style.display = 'flex';
} else {
counterElement.style.display = 'none';
}
}
function applyBellSize(sizeEm) {
if (bellElement) {
bellElement.style.fontSize = `${sizeEm}em`;
positionBellIcon();
}
}
function applyBellPosition(top, left) {
if (bellElement) {
bellElement.style.position = 'absolute';
bellElement.style.top = top;
bellElement.style.left = left;
bellElement.style.transform = '';
}
}
function applyPanelPosition(panel, savedPos) {
if (!panel || !savedPos) return;
if (savedPos.top !== null && savedPos.left !== null) {
panel.style.position = 'fixed';
panel.style.top = savedPos.top;
panel.style.left = savedPos.left;
panel.style.right = 'unset';
panel.style.bottom = 'unset';
}
}
function savePanelPosition(panel, lsKeyTop, lsKeyLeft, savedPosObject) {
if (!panel || !panel.parentNode) return;
const currentTop = panel.style.top;
const currentLeft = panel.style.left;
if (currentTop && currentLeft) {
localStorage.setItem(lsKeyTop, currentTop);
localStorage.setItem(lsKeyLeft, currentLeft);
if (savedPosObject) {
savedPosObject.top = currentTop;
savedPosObject.left = currentLeft;
}
}
}
function dismissNotification(idToDismiss) {
const initialLength = evilBoxHistory.length;
evilBoxHistory = evilBoxHistory.filter(entry => entry.id !== idToDismiss);
const removed = initialLength > evilBoxHistory.length;
if (removed && historyPanel) {
const itemElement = historyPanel.querySelector(`.history-item[data-id="${idToDismiss}"]`);
if (itemElement) { itemElement.remove(); }
filterNotifications(historyPanel.querySelector('.filter-button-container'));
}
}
function createNotificationElement(entry) {
const itemDiv = document.createElement('div');
itemDiv.className = 'history-item';
itemDiv.dataset.id = entry.id;
itemDiv.dataset.categories = JSON.stringify(entry.categories || []);
const timeSpan = document.createElement('span');
timeSpan.className = 'history-timestamp';
const timeOptions = { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' };
try {
timeSpan.textContent = entry.timestamp.toLocaleTimeString([], timeOptions);
} catch (e) {
timeSpan.textContent = entry.timestamp.toLocaleTimeString();
}
const contentDiv = document.createElement('div');
contentDiv.className = 'history-evilbox-content';
const template = document.createElement('template');
try {
if (entry.html && entry.html.trim()) {
template.innerHTML = entry.html.trim();
let contentToAppend = template.content.querySelector('div.evilBox') || template.content.firstChild;
if (contentToAppend) {
if (contentToAppend.nodeName.toLowerCase() === 'evilbox' || contentToAppend.classList?.contains('evilBox')) {
contentToAppend.style.display = 'block';
}
contentDiv.appendChild(contentToAppend.cloneNode(true));
} else {
contentDiv.innerHTML = '<i>Contenu invalide ou vide après parsing.</i>';
}
} else {
contentDiv.innerHTML = '<i>Contenu vide.</i>';
}
} catch (e) {
contentDiv.innerHTML = '<i>Erreur d\'affichage.</i>';
}
const dismissBtn = document.createElement('button');
dismissBtn.className = 'dismiss-btn'; dismissBtn.innerHTML = '×'; dismissBtn.title = 'Supprimer cette notification';
dismissBtn.addEventListener('click', (e) => { e.stopPropagation(); dismissNotification(entry.id); });
itemDiv.appendChild(timeSpan);
itemDiv.appendChild(contentDiv);
itemDiv.appendChild(dismissBtn);
return itemDiv;
}
function handleFilterButtonClick(categoryKey, filterAreaElement) {
if (categoryKey === 'Tous') {
Object.keys(categoryStates).forEach(cat => categoryStates[cat] = 'inactive');
} else {
const currentState = categoryStates[categoryKey] || 'inactive';
if (currentState === 'inactive') {
categoryStates[categoryKey] = 'active';
} else if (currentState === 'active') {
categoryStates[categoryKey] = 'excluded';
} else {
categoryStates[categoryKey] = 'inactive';
}
}
saveCategoryStates();
filterNotifications(filterAreaElement);
}
function removeCategoryFilterAndRebuild(categoryKeyToRemove, filterAreaElement) {
notificationCategories.delete(categoryKeyToRemove);
delete categoryStates[categoryKeyToRemove];
saveNotificationCategories();
saveCategoryStates();
rebuildFilterButtonsUI(filterAreaElement);
filterNotifications(filterAreaElement);
}
function rebuildFilterButtonsUI(filterAreaElement) {
filterAreaElement.innerHTML = '';
const createFilterButtonElement = (categoryKey, text) => {
const button = document.createElement('button');
button.className = 'filter-button';
button.dataset.category = categoryKey;
const textSpan = document.createElement('span');
textSpan.className = 'filter-button-text';
textSpan.textContent = text;
button.appendChild(textSpan);
button.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-category-btn')) {
return;
}
handleFilterButtonClick(categoryKey, filterAreaElement);
});
if (categoryKey !== 'Tous') {
const removeBtn = document.createElement('span');
removeBtn.className = 'remove-category-btn';
removeBtn.innerHTML = '×';
removeBtn.title = `Supprimer le filtre '${text}'`;
removeBtn.addEventListener('click', (e) => {
e.stopPropagation();
removeCategoryFilterAndRebuild(categoryKey, filterAreaElement);
});
button.appendChild(removeBtn);
}
return button;
};
filterAreaElement.appendChild(createFilterButtonElement('Tous', 'Tous'));
Array.from(notificationCategories).sort().forEach(category => {
const displayText = category.charAt(0).toUpperCase() + category.slice(1);
filterAreaElement.appendChild(createFilterButtonElement(category, displayText));
});
}
function filterNotifications(filterButtonContainer) {
if (!historyPanel || !filterButtonContainer) return;
const buttons = filterButtonContainer.querySelectorAll('.filter-button');
buttons.forEach(btn => {
const cat = btn.dataset.category;
btn.classList.remove('active', 'excluded');
if (cat === 'Tous') {
const hasActiveOrExcludedFilter = Object.keys(categoryStates).some(key => notificationCategories.has(key) && categoryStates[key] !== 'inactive');
if (!hasActiveOrExcludedFilter) btn.classList.add('active');
} else if (categoryStates[cat] === 'active') {
btn.classList.add('active');
} else if (categoryStates[cat] === 'excluded') {
btn.classList.add('excluded');
}
});
const contentArea = historyPanel.querySelector('.history-content');
if (!contentArea) return;
const activeFilters = Object.entries(categoryStates).filter(([, state]) => state === 'active').map(([cat]) => cat);
const excludedFilters = Object.entries(categoryStates).filter(([, state]) => state === 'excluded').map(([cat]) => cat);
const items = contentArea.querySelectorAll('.history-item');
let hasVisibleItems = false;
items.forEach(item => {
let itemSpecificCategories = [];
try { itemSpecificCategories = JSON.parse(item.dataset.categories || '[]'); } catch(e) {}
const isExcluded = itemSpecificCategories.some(specificCat => {
const effectiveCat = getEffectiveCategoryKey(specificCat);
return excludedFilters.includes(effectiveCat);
});
if (isExcluded) {
item.style.display = 'none';
return;
}
let matchesActive;
if (activeFilters.length > 0) {
matchesActive = itemSpecificCategories.some(specificCat => {
const effectiveCat = getEffectiveCategoryKey(specificCat);
return activeFilters.includes(effectiveCat);
});
} else {
matchesActive = true;
}
if (matchesActive) {
item.style.display = '';
hasVisibleItems = true;
} else {
item.style.display = 'none';
}
});
const emptyMsgElement = contentArea.querySelector('.empty-filter-message');
if (emptyMsgElement) { emptyMsgElement.remove(); }
if (evilBoxHistory.length === 0) {
const existingEmptyMsg = contentArea.querySelector('p > i');
if (!existingEmptyMsg || !existingEmptyMsg.textContent.startsWith("Aucune notification détectée")) {
contentArea.innerHTML = "<p><i>Aucune notification détectée pour le moment.</i></p>";
}
} else if (!hasVisibleItems) {
const p = document.createElement('p');
p.className = 'empty-filter-message';
p.innerHTML = `<i>Aucune notification ne correspond aux filtres sélectionnés.</i>`;
contentArea.appendChild(p);
}
}
function toggleFiltersArea(button, areaWrapper) {
filtersCollapsed = !filtersCollapsed;
localStorage.setItem(LS_FILTERS_COLLAPSED, filtersCollapsed);
areaWrapper.classList.toggle('collapsed', filtersCollapsed);
button.textContent = filtersCollapsed ? 'Afficher catégories ►' : 'Masquer catégories ▼';
}
function showHistoryPanel() {
closeSettingsPanel();
unreadCount = 0;
updateUnreadCounterDisplay();
if (!historyPanel) {
historyPanel = document.createElement('div');
historyPanel.id = 'evilbox-history-panel';
const header = document.createElement('div');
header.className = 'history-header';
const titleSpan = document.createElement('span');
titleSpan.textContent = "Historique des Notifications";
const controlsDiv = document.createElement('div');
controlsDiv.className = 'history-header-controls';
const settingsBtn = document.createElement('button');
settingsBtn.className = 'settings-btn'; settingsBtn.innerHTML = '⚙️'; settingsBtn.title = 'Ouvrir les paramètres';
settingsBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleSettingsPanel(); });
const closeBtn = document.createElement('button');
closeBtn.className = 'close-btn'; closeBtn.innerHTML = '×'; closeBtn.title = 'Fermer l\'historique';
closeBtn.addEventListener('click', (e) => { e.stopPropagation(); closeHistoryPanel(); });
controlsDiv.appendChild(settingsBtn); controlsDiv.appendChild(closeBtn);
header.appendChild(titleSpan); header.appendChild(controlsDiv);
historyPanel.appendChild(header);
header.addEventListener('mousedown', (e) => dragStart(e, historyPanel));
const filtersToggleAndContainer = document.createElement('div');
filtersToggleAndContainer.className = 'filters-toggle-container';
const toggleFiltersButton = document.createElement('button');
toggleFiltersButton.className = 'toggle-filters-btn';
filtersToggleAndContainer.appendChild(toggleFiltersButton);
const filterButtonsWrapper = document.createElement('div');
filterButtonsWrapper.className = 'filter-buttons-wrapper';
if (filtersCollapsed) {
filterButtonsWrapper.classList.add('collapsed');
}
toggleFiltersButton.textContent = filtersCollapsed ? 'Afficher catégories ►' : 'Masquer catégories ▼';
const filterArea = document.createElement('div');
filterArea.className = 'filter-button-container';
toggleFiltersButton.addEventListener('click', () => toggleFiltersArea(toggleFiltersButton, filterButtonsWrapper));
rebuildFilterButtonsUI(filterArea);
filterButtonsWrapper.appendChild(filterArea);
filtersToggleAndContainer.appendChild(filterButtonsWrapper);
historyPanel.appendChild(filtersToggleAndContainer);
const contentArea = document.createElement('div');
contentArea.className = 'history-content';
historyPanel.appendChild(contentArea);
[...evilBoxHistory].reverse().forEach((entry) => {
const itemElement = createNotificationElement(entry);
contentArea.appendChild(itemElement);
});
filterNotifications(filterArea);
document.body.appendChild(historyPanel);
applyPanelPosition(historyPanel, savedHistoryPanelPos);
}
}
function closeHistoryPanel() {
if (historyPanel && historyPanel.parentNode) {
savePanelPosition(historyPanel, LS_HISTORY_PANEL_POS_TOP, LS_HISTORY_PANEL_POS_LEFT, savedHistoryPanelPos);
historyPanel.parentNode.removeChild(historyPanel);
historyPanel = null;
closeSettingsPanel();
}
}
function toggleHistoryPanel() {
if (historyPanel && historyPanel.parentNode) {
closeHistoryPanel();
} else {
showHistoryPanel();
}
}
function updateOriginalNotificationVisibility() {
if (hideOriginalNotifications) {
document.body.classList.add(BODY_CLASS_HIDE_ORIGINAL);
} else {
document.body.classList.remove(BODY_CLASS_HIDE_ORIGINAL);
}
}
function resetBellPosition() {
if (bellElement) {
savedBellPos.top = null;
savedBellPos.left = null;
localStorage.removeItem(LS_BELL_POS_TOP);
localStorage.removeItem(LS_BELL_POS_LEFT);
positionBellIcon();
}
}
function createSettingsPanel() {
if (settingsPanel) return;
settingsPanel = document.createElement('div');
settingsPanel.id = 'evilbox-settings-panel';
const header = document.createElement('div');
header.className = 'settings-header';
header.innerHTML = `<span>Paramètres</span><button class="close-btn" title="Fermer les paramètres">×</button>`;
settingsPanel.appendChild(header);
header.querySelector('.close-btn').addEventListener('click', (e) => {
e.stopPropagation();
closeSettingsPanel();
});
header.addEventListener('mousedown', (e) => dragStart(e, settingsPanel));
const content = document.createElement('div');
content.className = 'settings-content';
content.appendChild(createSettingSectionTitle('Apparence'));
content.appendChild(createSettingItem(
'Taille de la cloche (em):',
`<input type="range" id="setting-bell-size" min="1.5" max="4.5" step="0.1" value="${currentBellSize}"> <span id="bell-size-value">${currentBellSize}em</span>`
));
content.appendChild(createSettingItem(
'Position de la cloche:',
`<button id="setting-move-bell">Activer le déplacement</button>`
));
content.appendChild(createSettingItem(
'Masquer Notifications originales:',
`<label class="switch"><input type="checkbox" id="setting-hide-original" ${hideOriginalNotifications ? 'checked' : ''}><span class="slider round"></span></label>`
));
content.appendChild(createSettingSectionTitle('Son Notification'));
content.appendChild(createSettingItem(
'URL Son:',
`<input type="text" id="setting-notify-sound-url" value="${currentNotifySoundUrl === DEFAULT_NOTIFY_SOUND_URL ? '' : currentNotifySoundUrl}" placeholder="URL .mp3, .wav, .ogg (ou laisser vide)">`
));
content.appendChild(createSettingItem(
'Volume Son:',
`<input type="range" id="setting-notify-sound-volume" min="0" max="1" step="0.05" value="${currentNotifyVolume}">
<span id="notify-volume-value">${Math.round(currentNotifyVolume * 100)}%</span>
<button class="test-sound-btn" title="Tester le son">🔊</button>`
));
settingsPanel.appendChild(content);
document.body.appendChild(settingsPanel);
applyPanelPosition(settingsPanel, savedSettingsPanelPos);
const bellSizeInput = settingsPanel.querySelector('#setting-bell-size');
const bellSizeValueSpan = settingsPanel.querySelector('#bell-size-value');
bellSizeInput.addEventListener('input', () => {
currentBellSize = parseFloat(bellSizeInput.value);
bellSizeValueSpan.textContent = `${currentBellSize}em`;
applyBellSize(currentBellSize);
localStorage.setItem(LS_BELL_SIZE, currentBellSize);
});
const moveBellButton = settingsPanel.querySelector('#setting-move-bell');
moveBellButton.addEventListener('click', toggleBellMoveMode);
const hideOriginalCheckbox = settingsPanel.querySelector('#setting-hide-original');
hideOriginalCheckbox.addEventListener('change', () => {
hideOriginalNotifications = hideOriginalCheckbox.checked;
localStorage.setItem(LS_HIDE_ORIGINAL_NOTIFS, hideOriginalNotifications);
updateOriginalNotificationVisibility();
});
const notifySoundUrlInput = settingsPanel.querySelector('#setting-notify-sound-url');
notifySoundUrlInput.addEventListener('change', () => {
const newUrl = notifySoundUrlInput.value.trim();
if (!newUrl) {
currentNotifySoundUrl = DEFAULT_NOTIFY_SOUND_URL;
localStorage.setItem(LS_NOTIFY_SOUND_URL, currentNotifySoundUrl);
notifyAlertAudio = null;
notifySoundUrlInput.value = '';
} else if (newUrl.startsWith('http://') || newUrl.startsWith('https://')) {
currentNotifySoundUrl = newUrl;
localStorage.setItem(LS_NOTIFY_SOUND_URL, currentNotifySoundUrl);
notifyAlertAudio = null;
} else {
notifySoundUrlInput.value = (currentNotifySoundUrl === DEFAULT_NOTIFY_SOUND_URL) ? '' : currentNotifySoundUrl;
}
});
const notifyVolumeInput = settingsPanel.querySelector('#setting-notify-sound-volume');
const notifyVolumeValueSpan = settingsPanel.querySelector('#notify-volume-value');
notifyVolumeInput.addEventListener('input', () => {
currentNotifyVolume = parseFloat(notifyVolumeInput.value);
notifyVolumeValueSpan.textContent = `${Math.round(currentNotifyVolume * 100)}%`;
localStorage.setItem(LS_NOTIFY_SOUND_VOLUME, currentNotifyVolume);
});
content.addEventListener('click', (event) => {
if (event.target.classList.contains('test-sound-btn')) {
const url = notifySoundUrlInput.value.trim() || DEFAULT_NOTIFY_SOUND_URL;
const volume = notifyVolumeInput.value;
testSound(url, volume);
}
});
}
function createSettingSectionTitle(title) {
const titleDiv = document.createElement('div');
titleDiv.className = 'setting-section-title';
titleDiv.textContent = title;
return titleDiv;
}
function createSettingItem(label, controlHtml) {
const itemDiv = document.createElement('div');
itemDiv.className = 'setting-item';
itemDiv.innerHTML = `<label>${label}</label><div class="setting-control">${controlHtml}</div>`;
return itemDiv;
}
function closeSettingsPanel() {
if (settingsPanel && settingsPanel.parentNode) {
if (isBellMoveModeActive) {
toggleBellMoveMode();
}
savePanelPosition(settingsPanel, LS_SETTINGS_PANEL_POS_TOP, LS_SETTINGS_PANEL_POS_LEFT, savedSettingsPanelPos);
settingsPanel.parentNode.removeChild(settingsPanel);
settingsPanel = null;
}
}
function toggleSettingsPanel() {
if (settingsPanel && settingsPanel.parentNode) {
closeSettingsPanel();
} else {
if (historyPanel && historyPanel.parentNode) {
closeHistoryPanel();
}
createSettingsPanel();
}
}
function dragStart(e, panelElement) {
if (e.button !== 0 || e.target.closest('button, input, select, textarea, .filter-button, .switch, .toggle-filters-btn, .remove-category-btn')) return;
isDraggingPanel = true;
const targetPanel = panelElement;
initialMouseX = e.clientX;
initialMouseY = e.clientY;
const styles = window.getComputedStyle(targetPanel);
const currentTop = targetPanel.style.top || styles.top;
const currentLeft = targetPanel.style.left || styles.left;
initialPanelTop = parseFloat(currentTop) || 50;
initialPanelLeft = parseFloat(currentLeft) || (window.innerWidth - (parseFloat(styles.width) || 350) - 100);
targetPanel.style.position = 'fixed';
targetPanel.style.top = `${initialPanelTop}px`;
targetPanel.style.left = `${initialPanelLeft}px`;
targetPanel.style.right = 'unset';
targetPanel.style.bottom = 'unset';
e.preventDefault();
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
targetPanel.classList.add('dragging');
}
function dragMove(e) {
if (!isDraggingPanel) return;
const targetPanel = (settingsPanel && settingsPanel.classList.contains('dragging')) ? settingsPanel : historyPanel;
if (!targetPanel) return;
const dx = e.clientX - initialMouseX;
const dy = e.clientY - initialMouseY;
targetPanel.style.top = `${initialPanelTop + dy}px`;
targetPanel.style.left = `${initialPanelLeft + dx}px`;
}
function dragEnd(e) {
if (!isDraggingPanel) return;
isDraggingPanel = false;
const targetPanel = (settingsPanel && settingsPanel.classList.contains('dragging')) ? settingsPanel : historyPanel;
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
if (targetPanel) {
targetPanel.classList.remove('dragging');
}
}
function toggleBellMoveMode() {
isBellMoveModeActive = !isBellMoveModeActive;
const button = settingsPanel?.querySelector('#setting-move-bell');
if (!bellElement || !button) return;
if (isBellMoveModeActive) {
bellElement.classList.add('bell-moving');
bellElement.addEventListener('mousedown', bellDragStart);
button.textContent = 'Arrêter le déplacement';
button.classList.add('active');
} else {
bellElement.classList.remove('bell-moving');
bellElement.removeEventListener('mousedown', bellDragStart);
button.textContent = 'Activer le déplacement';
button.classList.remove('active');
saveBellPosition();
}
}
function bellDragStart(e) {
if (e.button !== 0 || !isBellMoveModeActive) return;
e.preventDefault();
e.stopPropagation();
isDraggingBell = true;
initialBellDragX = e.clientX;
initialBellDragY = e.clientY;
initialBellLeft = bellElement.offsetLeft;
initialBellTop = bellElement.offsetTop;
bellElement.style.cursor = 'grabbing';
document.addEventListener('mousemove', bellDragMove);
document.addEventListener('mouseup', bellDragEnd);
}
function bellDragMove(e) {
if (!isDraggingBell) return;
e.preventDefault();
const dx = e.clientX - initialBellDragX;
const dy = e.clientY - initialBellDragY;
const newTop = initialBellTop + dy;
const newLeft = initialBellLeft + dx;
bellElement.style.left = `${newLeft}px`;
bellElement.style.top = `${newTop}px`;
}
function bellDragEnd(e) {
if (!isDraggingBell) return;
e.preventDefault();
isDraggingBell = false;
bellElement.style.cursor = 'pointer';
document.removeEventListener('mousemove', bellDragMove);
document.removeEventListener('mouseup', bellDragEnd);
}
function saveBellPosition() {
if (bellElement) {
const finalTop = bellElement.style.top;
const finalLeft = bellElement.style.left;
localStorage.setItem(LS_BELL_POS_TOP, finalTop);
localStorage.setItem(LS_BELL_POS_LEFT, finalLeft);
savedBellPos.top = finalTop;
savedBellPos.left = finalLeft;
}
}
function positionBellIcon() {
if (!bellElement) return;
const logoutLi = document.querySelector('li.logout');
const parentUl = logoutLi ? logoutLi.closest('ul') : null;
if (!logoutLi || !parentUl) return;
if (savedBellPos.top !== null && savedBellPos.left !== null) {
applyBellPosition(savedBellPos.top, savedBellPos.left);
} else {
requestAnimationFrame(() => {
try {
const logoutLiRect = logoutLi.getBoundingClientRect();
const parentRect = parentUl.getBoundingClientRect();
const bellRect = bellElement.getBoundingClientRect();
const topOffset = (logoutLiRect.bottom - parentRect.top) + BELL_MARGIN_TOP;
const bellWidth = bellRect.width;
const effectiveBellWidth = Math.max(1, bellWidth);
const leftOffset = (logoutLiRect.left + logoutLiRect.width / 2) - parentRect.left - (effectiveBellWidth / 2);
applyBellPosition(`${topOffset}px`, `${leftOffset}px`);
} catch(e) {
applyBellPosition('60px', '20px');
}
});
}
bellElement.style.visibility = 'visible';
}
function addBellIcon() {
clearTimeout(addBellIconTimeout);
if (document.getElementById('evilbox-history-bell')) return;
const logoutLi = document.querySelector('li.logout');
const parentUl = logoutLi ? logoutLi.closest('ul') : null;
if (!logoutLi || !parentUl) {
addBellIconRetryCount++;
if (addBellIconRetryCount <= MAX_RETRIES) {
addBellIconTimeout = setTimeout(addBellIcon, RETRY_DELAY);
}
return;
}
addBellIconRetryCount = 0;
const parentStyle = window.getComputedStyle(parentUl);
if (parentStyle.position === 'static') {
parentUl.style.position = 'relative';
}
bellElement = document.createElement('li');
bellElement.id = 'evilbox-history-bell';
bellElement.className = 'link couleur5';
bellElement.title = "Afficher/Masquer l'historique des notifications";
bellElement.style.position = 'absolute';
bellElement.style.cursor = 'pointer';
bellElement.style.zIndex = SCRIPT_Z_INDEX_BASE - 1;
bellElement.style.padding = '2px 5px';
bellElement.style.margin = '0';
bellElement.style.listStyle = 'none';
bellElement.style.whiteSpace = 'nowrap';
bellElement.style.visibility = 'hidden';
applyBellSize(currentBellSize);
const bellEmojiSpan = document.createElement('span');
bellEmojiSpan.textContent = '🔔';
bellElement.appendChild(bellEmojiSpan);
counterElement = document.createElement('span');
counterElement.id = 'evilbox-unread-counter';
counterElement.style.display = 'none';
bellElement.appendChild(counterElement);
parentUl.appendChild(bellElement);
bellElement.addEventListener('animationend', () => {
if (bellElement) bellElement.classList.remove('shake');
});
bellElement.addEventListener('click', toggleHistoryPanel);
positionBellIcon();
updateUnreadCounterDisplay();
}
function initializeKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
if (e.altKey && e.key.toLowerCase() === 'p') {
e.preventDefault();
resetBellPosition();
}
});
}
function processDismissQueue() {
if (evilBoxDismissQueue.size === 0) {
return;
}
const idsToDismiss = Array.from(evilBoxDismissQueue);
evilBoxDismissQueue.clear();
idsToDismiss.forEach(evilBoxId => {
const liveEvilBox = document.getElementById(evilBoxId);
if (liveEvilBox && document.body.contains(liveEvilBox)) {
if (typeof $ === 'function' && $(liveEvilBox).length) {
$(liveEvilBox).trigger('click');
} else if (typeof liveEvilBox.click === 'function') {
liveEvilBox.click();
}
}
});
}
const originalXhrOpen = XMLHttpRequest.prototype.open;
const originalXhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._requestMethod = method;
this._requestUrl = url;
return originalXhrOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
if (this._requestMethod?.toUpperCase() === 'POST' && this._requestUrl?.includes(CHECK_URL)) {
this.addEventListener('load', function() {
if (this.readyState === 4 && this.status === 200 && this.responseText) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(this.responseText, "text/html");
const allEvilBoxDivs = doc.querySelectorAll('div.evilBox');
let soundPlayedThisCycle = false;
const newEntriesForThisCheck = [];
const baseTimestamp = Date.now();
let newCategoriesDiscoveredInThisBatch = false;
let newAlertableNotificationsCount = 0;
allEvilBoxDivs.forEach((boxDiv, index) => {
const parentEvilBoxElement = boxDiv.closest('evilbox') || boxDiv;
let historyHtml = parentEvilBoxElement.outerHTML;
const originalEntryCategories = Array.from(boxDiv.classList).filter(cls =>
cls !== 'evilBox' && !cls.startsWith('eb_') && !cls.startsWith('un-') && cls.trim() !== ''
);
originalEntryCategories.forEach(cat => {
const effectiveCat = getEffectiveCategoryKey(cat);
if (!notificationCategories.has(effectiveCat)) {
notificationCategories.add(effectiveCat);
categoryStates[effectiveCat] = 'inactive';
newCategoriesDiscoveredInThisBatch = true;
}
});
const uniqueId = `${baseTimestamp}-${index}`;
const historyEntry = { id: uniqueId, timestamp: new Date(), html: historyHtml, categories: originalEntryCategories };
evilBoxHistory.push(historyEntry);
newEntriesForThisCheck.push(historyEntry);
if (evilBoxHistory.length > MAX_HISTORY_ENTRIES) { evilBoxHistory.shift(); }
const isExcludedByFilter = originalEntryCategories.some(specificCat => {
const effectiveCat = getEffectiveCategoryKey(specificCat);
return categoryStates[effectiveCat] === 'excluded';
});
if (hideOriginalNotifications && isExcludedByFilter) {
const evilBoxId = boxDiv.id;
if (evilBoxId) {
evilBoxDismissQueue.add(evilBoxId);
}
}
if (!isExcludedByFilter) {
newAlertableNotificationsCount++;
if (!soundPlayedThisCycle && hideOriginalNotifications) {
playNotificationSound();
soundPlayedThisCycle = true;
}
}
});
if (newCategoriesDiscoveredInThisBatch) {
saveNotificationCategories();
saveCategoryStates();
}
if (newEntriesForThisCheck.length > 0) {
if (newAlertableNotificationsCount > 0 && bellElement) {
bellElement.classList.remove('shake');
void bellElement.offsetWidth;
bellElement.classList.add('shake');
}
if (historyPanel && historyPanel.parentNode) {
const contentArea = historyPanel.querySelector('.history-content');
if (contentArea) {
if (newCategoriesDiscoveredInThisBatch) {
loadCategoryStates();
const filterArea = historyPanel.querySelector('.filter-button-container');
if (filterArea) {
rebuildFilterButtonsUI(filterArea);
}
}
newEntriesForThisCheck.reverse().forEach(entry => {
const itemElement = createNotificationElement(entry);
contentArea.prepend(itemElement);
});
const historyItemsInDOM = contentArea.querySelectorAll('.history-item');
if (historyItemsInDOM.length > MAX_HISTORY_ENTRIES) {
for (let i = MAX_HISTORY_ENTRIES; i < historyItemsInDOM.length; i++) { historyItemsInDOM[i].remove(); }
}
filterNotifications(historyPanel.querySelector('.filter-button-container'));
}
} else {
unreadCount += newAlertableNotificationsCount;
updateUnreadCounterDisplay();
}
}
} catch (e) {}
}
});
}
return originalXhrSend.apply(this, arguments);
};
GM_addStyle(`
@keyframes shake {
10%, 90% { transform: translate3d(-2px, 0, 0) rotate(-2deg); }
20%, 80% { transform: translate3d(4px, 0, 0) rotate(4deg); }
30%, 50%, 70% { transform: translate3d(-6px, 0, 0) rotate(-6deg); }
40%, 60% { transform: translate3d(6px, 0, 0) rotate(6deg); }
}
.shake {
animation: shake ${SHAKE_ANIMATION_DURATION / 1000}s cubic-bezier(.36,.07,.19,.97) both;
transform-origin: center bottom;
backface-visibility: hidden; perspective: 1000px;
}
body.${BODY_CLASS_HIDE_ORIGINAL} > evilbox,
body.${BODY_CLASS_HIDE_ORIGINAL} > div.evilBox,
body.${BODY_CLASS_HIDE_ORIGINAL} #zone_evilBox {
position: absolute !important; left: -9999px !important; top: -9999px !important;
opacity: 0 !important; pointer-events: none !important; visibility: hidden !important;
width: 1px !important; height: 1px !important; overflow: hidden !important;
display: block !important;
z-index: -1 !important;
}
#evilbox-history-bell {
line-height: 1 !important;
vertical-align: middle;
user-select: none;
transition: transform 0.2s ease-out, opacity 0.2s ease;
transform-origin: center center;
}
#evilbox-history-bell > span:first-of-type {
vertical-align: middle; display: inline-block;
transition: transform 0.2s ease;
}
#evilbox-history-bell.bell-moving {
border: 2px dashed #FFEB3B;
background-color: rgba(255, 235, 59, 0.25);
cursor: grabbing !important;
box-shadow: 0 0 8px rgba(255, 235, 59, 0.5);
}
#evilbox-history-bell:hover:not(.bell-moving) {
transform: scale(1.1);
}
#evilbox-unread-counter {
position: absolute;
bottom: -10px;
left: -14px;
font-size: 16px;
min-width: 27px;
height: 27px;
background-color: red; color: white;
border-radius: 50%;
font-weight: bold;
display: flex; justify-content: center; align-items: center;
line-height: 1;
z-index: ${SCRIPT_Z_INDEX_BASE};
pointer-events: none;
box-sizing: border-box;
user-select: none;
}
#evilbox-history-panel, #evilbox-settings-panel {
position: fixed;
width: 350px;
max-height: 80vh;
background-color: #2f2f2f;
border: 1px solid #777;
border-radius: 6px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
z-index: ${SCRIPT_Z_INDEX_BASE};
color: #ddd;
font-size: 14px;
display: flex; flex-direction: column;
overflow: hidden;
box-sizing: border-box;
cursor: default;
}
#evilbox-history-panel { top: 50px; right: 100px; left: unset; }
#evilbox-settings-panel { top: 60px; right: 120px; left: unset; z-index: ${SCRIPT_Z_INDEX_BASE + 1}; }
#evilbox-history-panel.dragging, #evilbox-settings-panel.dragging {
cursor: grabbing !important;
user-select: none;
opacity: 0.9;
}
#evilbox-history-panel[style*="visibility: hidden"],
#evilbox-settings-panel[style*="visibility: hidden"] { visibility: visible !important; }
#evilbox-history-panel[style*="display: none"],
#evilbox-settings-panel[style*="display: none"] { display: flex !important; }
.history-header, .settings-header {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 15px;
background-color: #3a3a3a;
border-bottom: 1px solid #555;
font-weight: bold;
flex-shrink: 0;
cursor: grab;
user-select: none;
}
.history-header:active, .settings-header:active { cursor: grabbing; }
.history-header-controls { display: flex; align-items: center; gap: 8px; }
.history-header .close-btn, .settings-header .close-btn,
.history-header .settings-btn {
background: none; border: none; color: #ccc;
font-size: 1.6em;
line-height: 1; cursor: pointer; padding: 0 5px;
font-weight: bold; transition: color 0.2s ease;
vertical-align: middle;
}
.history-header .settings-btn { font-size: 1.3em; }
.history-header .close-btn:hover, .settings-header .close-btn:hover,
.history-header .settings-btn:hover { color: #fff; }
.filters-toggle-container {
background-color: #333;
border-bottom: 1px solid #555;
flex-shrink: 0;
}
.toggle-filters-btn {
background-color: transparent;
color: #ccc;
border: none;
padding: 6px 12px;
width: 100%;
text-align: left;
font-size: 0.9em;
cursor: pointer;
user-select: none;
transition: background-color 0.2s;
}
.toggle-filters-btn:hover {
background-color: #404040;
}
.filter-buttons-wrapper {
max-height: 300px;
overflow-y: auto;
transition: max-height 0.3s ease-out, padding 0.3s ease-out, opacity 0.3s ease-out;
padding: 8px 12px;
opacity: 1;
}
.filter-buttons-wrapper.collapsed {
max-height: 0;
padding-top: 0;
padding-bottom: 0;
opacity: 0;
overflow: hidden;
}
.filter-button-container {
display: flex; flex-wrap: wrap; gap: 8px;
}
.filter-button {
background-color: #555; color: #ddd; border: 1px solid #777; border-radius: 4px;
padding: 4px 6px 4px 10px; font-size: 0.9em; cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; user-select: none;
display: inline-flex; align-items: center;
}
.filter-button:hover { background-color: #666; border-color: #888; }
.filter-button.active {
background-color: #8ab4f8;
color: #1a1a1a;
border-color: #8ab4f8; font-weight: bold;
}
.filter-button.excluded {
background-color: #f28b82;
color: #1a1a1a;
border-color: #f28b82; font-weight: bold;
}
.filter-button.excluded .filter-button-text {
text-decoration: line-through;
}
.filter-button-text {
margin-right: 4px;
flex-grow: 1;
text-align: left;
}
.remove-category-btn {
font-size: 1.1em;
font-weight: bold;
color: #aaa;
margin-left: 5px;
padding: 0 4px;
border-radius: 3px;
line-height: 1;
cursor: pointer;
transition: color 0.2s ease, background-color 0.2s ease;
}
.filter-button:hover .remove-category-btn {
color: #ddd;
}
.remove-category-btn:hover {
background-color: rgba(0,0,0,0.2);
color: #fff !important;
}
.filter-button.active .remove-category-btn,
.filter-button.excluded .remove-category-btn {
color: #333;
}
.filter-button.active:hover .remove-category-btn,
.filter-button.excluded:hover .remove-category-btn {
color: #000;
}
#evilbox-history-panel .history-content {
padding: 5px 12px 10px 12px;
overflow-y: auto;
flex-grow: 1;
min-height: 50px;
}
#evilbox-history-panel .history-item {
position: relative;
border-bottom: 1px dashed #555;
padding: 10px 25px 10px 5px;
margin-bottom: 5px;
}
#evilbox-history-panel .history-item:last-child { border-bottom: none; padding-bottom: 5px; margin-bottom: 0; }
#evilbox-history-panel .history-timestamp {
display: block; font-size: 0.85em; color: #999;
margin-bottom: 6px;
}
#evilbox-history-panel .dismiss-btn {
position: absolute; top: 8px; right: 5px;
background: none; border: none; color: #888;
font-size: 1.5em; font-weight: bold; line-height: 1;
padding: 0 4px; cursor: pointer; opacity: 0.7;
z-index: 1;
transition: color 0.2s, opacity 0.2s;
}
#evilbox-history-panel .dismiss-btn:hover { color: #eee; opacity: 1; }
#evilbox-history-panel .empty-filter-message,
#evilbox-history-panel .history-content > p > i {
text-align: center; color: #888; margin-top: 15px; font-style: italic;
}
#evilbox-history-panel .history-evilbox-content evilbox,
#evilbox-history-panel .history-evilbox-content div.evilBox {
display: block !important; visibility: visible !important; position: relative !important;
left: auto !important; top: auto !important; opacity: 1 !important;
padding: 8px 10px; margin: 0 0 5px 0; border: 1px solid #444;
background-color: #383838; border-radius: 4px; min-height: 10px;
box-sizing: border-box; z-index: auto !important; width: auto !important;
height: auto !important; overflow: visible !important; color: #ccc;
font-size: 1em;
}
#evilbox-history-panel .history-evilbox-content .action { display: none !important; }
#evilbox-history-panel .history-evilbox-content .couleur4 { color: #FFC107 !important; }
#evilbox-history-panel .history-evilbox-content .couleur2 { color: #90EE90 !important; }
#evilbox-history-panel .history-evilbox-content .couleur5 { color: #bbb !important; }
#evilbox-history-panel .history-evilbox-content .link {
cursor: pointer !important; text-decoration: underline !important; color: #8ab4f8 !important;
}
#evilbox-history-panel .history-evilbox-content .link:hover { color: #a8c7fa !important; }
#evilbox-history-panel .history-evilbox-content .titre {
font-weight: bold !important; margin-bottom: 4px !important; display: block !important; color: #eee !important;
}
#evilbox-history-panel .history-evilbox-content .content {
margin-bottom: 4px !important; display: block !important; line-height: 1.4;
}
#evilbox-settings-panel .settings-content {
padding: 15px 20px; overflow-y: auto; flex-grow: 1;
}
.setting-section-title {
font-size: 1.1em; font-weight: bold; color: #eee; margin-top: 20px;
margin-bottom: 15px; padding-bottom: 5px; border-bottom: 1px solid #555;
}
.setting-section-title:first-of-type { margin-top: 0; }
.setting-item {
margin-bottom: 15px; display: flex; flex-direction: column; gap: 6px;
}
.setting-item label:not(.switch) {
font-weight: bold; color: #bbb; font-size: 0.95em;
display: block;
margin-bottom: 4px;
}
.setting-control {
display: flex; align-items: center; gap: 8px;
}
.setting-control input[type="range"] {
flex-grow: 1; cursor: pointer; margin: 0 5px;
}
.setting-control input[type="text"] {
flex-grow: 1; padding: 6px 9px; background-color: #444;
border: 1px solid #666; color: #ddd; border-radius: 4px;
font-size: 0.95em;
}
.setting-control input[type="text"]:focus {
outline: none; border-color: #8ab4f8; box-shadow: 0 0 3px rgba(138, 180, 248, 0.5);
}
.setting-control button:not(.test-sound-btn) {
background-color: #555; color: #ddd; border: 1px solid #777; border-radius: 4px;
padding: 6px 14px; font-size: 0.9em; cursor: pointer;
transition: background-color 0.2s ease; user-select: none;
}
.setting-control button:not(.test-sound-btn):hover { background-color: #666; }
.setting-control button#setting-move-bell.active {
background-color: #c8a03a; color: #1a1a1a; border-color: #c8a03a; font-weight: bold;
}
.setting-control span {
font-size: 0.9em; color: #aaa; min-width: 45px; text-align: right;
user-select: none;
}
.setting-control .test-sound-btn {
background: none; border: none; color: #bbb; font-size: 1.6em; line-height: 1;
padding: 0 5px; cursor: pointer; transition: color 0.2s ease;
order: 3; margin-left: 5px;
}
.setting-control .test-sound-btn:hover { color: #fff; }
.setting-control .test-sound-btn:active { transform: scale(0.9); }
.setting-control .switch {
position: relative;
display: inline-block;
width: 40px;
height: 14px;
margin: 2px 0;
}
.setting-control .switch input {
opacity: 0;
width: 0;
height: 0;
}
.setting-control .slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #393939;
transition: .2s ease-in-out;
border-radius: 7px;
border: 1px solid #555;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.15);
}
.setting-control .slider:before {
position: absolute;
content: "";
height: 10px;
width: 10px;
left: 2px;
top: 50%;
transform: translateY(-50%);
background-color: #fff;
transition: .2s ease-in-out;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}
.setting-control input:checked + .slider {
background-color: #2196F3;
border-color: #1976D2;
}
.setting-control input:checked + .slider:before {
transform: translateY(-50%) translateX(25px);
}
.setting-control input:focus + .slider {
border-color: #8ab4f8;
box-shadow: 0 0 1px #8ab4f8;
}
.setting-control .slider:hover {
border-color: #666;
background-color: #424242;
}
.setting-control input:checked + .slider:hover {
border-color: #1976D2;
background-color: #42A5F5;
}
.setting-item .setting-control label.switch {
margin: 0;
}
`);
function initializeScript() {
addBellIconRetryCount = 0;
addBellIcon();
initializeKeyboardShortcuts();
updateOriginalNotificationVisibility();
if (cleanupIntervalId === null) {
cleanupIntervalId = setInterval(processDismissQueue, PERIODIC_CLEANUP_INTERVAL);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeScript);
} else {
initializeScript();
}
})();