您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Auto refresh for HotSOS
当前为
// ==UserScript== // @name Auto Refresh Interface for Hotsauce // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description Auto refresh for HotSOS // @author PC // @match https://na4.m-tech.com/service-optimization/operations/service-orders/* // @run-at document-idle // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @license MIT // ==/UserScript== (function() { 'use strict'; // CSS Styles - Modified for better spacing between sections GM_addStyle(` #auto-refresh-container { position: fixed; top: 20px; left: 20px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 6px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); padding: 8px 10px; z-index: 9999; font-family: Arial, sans-serif; min-width: 150px; max-width: 200px; user-select: none; font-size: 12px; } #auto-refresh-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; /* Increased margin */ cursor: move; padding-bottom: 6px; /* Added padding */ border-bottom: 1px solid #eee; /* Added separator */ } #auto-refresh-title { font-weight: bold; color: #444; font-size: 12px; } #auto-refresh-controls { display: flex; align-items: center; gap: 8px; } #auto-refresh-collapse { cursor: pointer; font-size: 14px; color: #666; width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; border-radius: 50%; } #auto-refresh-collapse:hover { background-color: #eee; } #auto-refresh-body { overflow: hidden; transition: max-height 0.3s ease; } #auto-refresh-presets { padding-bottom: 8px; /* Added padding */ margin-bottom: 8px; /* Added margin */ border-bottom: 1px solid #eee; /* Added separator */ } .auto-refresh-preset { display: inline-block; background-color: #e9e9e9; border: none; border-radius: 4px; padding: 4px 8px; margin: 3px; cursor: pointer; transition: all 0.2s ease; font-size: 11px; } .auto-refresh-preset:hover { background-color: #d9d9d9; } .auto-refresh-preset.active { background-color: #4a89dc; color: white; } .auto-refresh-disabled .auto-refresh-preset { opacity: 0.5; cursor: default; } #auto-refresh-status { margin-top: 6px; font-size: 11px; color: #666; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-top: 2px; /* Added padding */ } #auto-refresh-status.warning { color: #f44336; } #auto-refresh-status.info { color: #9e9e9e; } #auto-refresh-status.success { color: #4caf50; } #auto-refresh-edit-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center; } #auto-refresh-edit-panel { background-color: white; border-radius: 6px; padding: 16px; width: 250px; box-shadow: 0 3px 14px rgba(0,0,0,0.2); } .auto-refresh-form-group { margin-bottom: 12px; } .auto-refresh-form-label { display: block; margin-bottom: 4px; font-weight: bold; font-size: 12px; } .auto-refresh-form-input { width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 12px; } .auto-refresh-form-buttons { display: flex; justify-content: flex-end; } .auto-refresh-form-button { padding: 6px 12px; border: none; border-radius: 4px; margin-left: 8px; cursor: pointer; font-size: 12px; } .auto-refresh-form-button.cancel { background-color: #f5f5f5; } .auto-refresh-form-button.save { background-color: #4a89dc; color: white; } .switch { position: relative; display: inline-block; width: 32px; height: 16px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .3s; border-radius: 16px; } .slider:before { position: absolute; content: ""; height: 12px; width: 12px; left: 2px; bottom: 2px; background-color: white; transition: .3s; border-radius: 50%; } input:checked + .slider { background-color: #4a89dc; } input:checked + .slider:before { transform: translateX(16px); } `); // Default presets with proper formatting const DEFAULT_PRESETS = [ { name: '15s', seconds: 15 }, { name: '30s', seconds: 30 }, { name: '1min', seconds: 60 }, { name: '5min', seconds: 300 } ]; // State management let state = { enabled: true, collapsed: false, activePreset: null, presets: GM_getValue('autoRefreshPresets', DEFAULT_PRESETS), position: GM_getValue('autoRefreshPosition', { x: 20, y: 20 }), refreshTimer: null, lastRefreshTime: null, editingPreset: null, onServiceOrdersPage: false, initialized: false, observerDebounce: false, refreshButtonObserved: false, isDragging: false }; // Check if we're on the service orders page function checkIfOnServiceOrdersPage() { try { // Check if URL matches the service orders page pattern const isServiceOrdersURL = window.location.href.includes('/service-optimization/operations/service-orders'); // Also check for refresh button as a fallback const refreshButton = findRefreshButton(); state.onServiceOrdersPage = isServiceOrdersURL && !!refreshButton; // If we're on the service orders page, observe the refresh button for manual clicks if (state.onServiceOrdersPage && !state.refreshButtonObserved && refreshButton) { observeRefreshButton(refreshButton); } return state.onServiceOrdersPage; } catch (error) { console.error('Error checking page type:', error); return false; } } // Observe the refresh button for manual clicks function observeRefreshButton(button) { try { if (!button || state.refreshButtonObserved) return; // Add click listener to track manual refreshes button.addEventListener('click', function manualRefreshHandler() { // Only update if auto-refresh is enabled if (state.enabled) { // Update time state.lastRefreshTime = new Date(); updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`); // Reset timer if we have an active preset if (state.activePreset && state.refreshTimer) { clearAutoRefresh(); state.refreshTimer = setInterval(() => { triggerRefresh(); }, state.activePreset.seconds * 1000); } } }); state.refreshButtonObserved = true; console.log('Refresh button observed for manual clicks'); } catch (error) { console.error('Error observing refresh button:', error); } } // Find refresh button with more detailed search function findRefreshButton() { try { // Try different button selectors const selectors = [ 'button[soe-data-cy="refresh"]', 'button[mat-icon-button] soe-icon[icon="refresh-dot"]', 'button[mat-icon-button] soe-icon[icon="refresh"]', 'button[aria-label="refresh"]', 'button.mat-icon-button mat-ripple.mat-button-ripple' ]; for (const selector of selectors) { const button = document.querySelector(selector); if (button) { return button; } } // If we can't find with specific selectors, try a broader approach const buttons = document.querySelectorAll('button'); for (const button of buttons) { const buttonHTML = button.innerHTML.toLowerCase(); if (buttonHTML.includes('refresh') || buttonHTML.includes('reload') || buttonHTML.includes('refresh-dot')) { return button; } } return null; } catch (error) { console.error('Error finding refresh button:', error); return null; } } // Format time as HH:MM:SS function formatTime(date) { if (!date) return 'Never'; return date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', second:'2-digit'}); } // Update status message - More concise now function updateStatus(type, message) { try { const statusEl = document.getElementById('auto-refresh-status'); if (statusEl) { statusEl.className = type || ''; statusEl.textContent = message || ''; } } catch (error) { console.error('Error updating status:', error); } } // Trigger refresh click function triggerRefresh() { try { const refreshButton = findRefreshButton(); if (refreshButton) { // If we found the button itself, click it refreshButton.click(); state.lastRefreshTime = new Date(); updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`); return true; } else { // If we couldn't find the button, show an error updateStatus('warning', 'Refresh button not found'); return false; } } catch (error) { console.error('Error triggering refresh:', error); updateStatus('warning', 'Error refreshing'); return false; } } // Start auto refresh with given preset function startAutoRefresh(preset) { try { // Clear existing timer clearAutoRefresh(); // Set active preset state.activePreset = preset; // Update UI updateActivePreset(); // Trigger a refresh immediately when setting a new preset if (state.onServiceOrdersPage) { triggerRefresh(); } // Only start timer if enabled and on service orders page if (state.enabled && state.onServiceOrdersPage) { state.refreshTimer = setInterval(() => { triggerRefresh(); }, preset.seconds * 1000); } // Save state saveState(); } catch (error) { console.error('Error starting auto refresh:', error); } } // Clear auto refresh timer function clearAutoRefresh() { try { if (state.refreshTimer) { clearInterval(state.refreshTimer); state.refreshTimer = null; } } catch (error) { console.error('Error clearing auto refresh:', error); } } // Update active preset highlighting function updateActivePreset() { try { // Remove active class from all presets document.querySelectorAll('.auto-refresh-preset').forEach(btn => { btn.classList.remove('active'); }); // Add active class to current preset if (state.activePreset) { const activeBtn = document.querySelector(`.auto-refresh-preset[data-seconds="${state.activePreset.seconds}"]`); if (activeBtn) { activeBtn.classList.add('active'); } } } catch (error) { console.error('Error updating active preset:', error); } } // Toggle auto refresh enabled state function toggleEnabled() { try { state.enabled = !state.enabled; // Update UI const container = document.getElementById('auto-refresh-container'); if (container) { if (state.enabled) { container.classList.remove('auto-refresh-disabled'); if (state.activePreset && state.onServiceOrdersPage) { startAutoRefresh(state.activePreset); } } else { container.classList.add('auto-refresh-disabled'); clearAutoRefresh(); updateStatus('info', 'Auto refresh is off'); } } // Save state saveState(); } catch (error) { console.error('Error toggling enabled state:', error); } } // Toggle collapsed state function toggleCollapsed() { try { state.collapsed = !state.collapsed; // Update UI const body = document.getElementById('auto-refresh-body'); const collapseBtn = document.getElementById('auto-refresh-collapse'); if (body && collapseBtn) { if (state.collapsed) { body.style.maxHeight = '0'; collapseBtn.textContent = '+'; } else { body.style.maxHeight = '500px'; collapseBtn.textContent = '-'; } } // Save state saveState(); } catch (error) { console.error('Error toggling collapsed state:', error); } } // Save state to GM storage function saveState() { try { GM_setValue('autoRefreshPresets', state.presets); GM_setValue('autoRefreshPosition', state.position); GM_setValue('autoRefreshEnabled', state.enabled); GM_setValue('autoRefreshCollapsed', state.collapsed); GM_setValue('autoRefreshActivePreset', state.activePreset); } catch (error) { console.error('Error saving state:', error); } } // Load state from GM storage function loadState() { try { state.presets = GM_getValue('autoRefreshPresets', DEFAULT_PRESETS); state.position = GM_getValue('autoRefreshPosition', { x: 20, y: 20 }); state.enabled = GM_getValue('autoRefreshEnabled', true); state.collapsed = GM_getValue('autoRefreshCollapsed', false); // Get the previously saved active preset, or default to 30s preset on first launch const savedActivePreset = GM_getValue('autoRefreshActivePreset', null); if (savedActivePreset) { state.activePreset = savedActivePreset; } else { // Find the 30s preset and set it as default state.activePreset = state.presets.find(preset => preset.seconds === 30) || state.presets[1]; } } catch (error) { console.error('Error loading state:', error); // Fallback to defaults state.presets = DEFAULT_PRESETS; state.position = { x: 20, y: 20 }; state.enabled = true; state.collapsed = false; // Set default preset to 30s state.activePreset = state.presets.find(preset => preset.seconds === 30) || state.presets[1]; } } // Format preset name based on seconds with improved logic function formatPresetName(seconds) { if (seconds < 60) { return `${seconds}s`; } else { return `${Math.floor(seconds / 60)}min`; } } // Show preset edit panel function showEditPanel(preset) { try { state.editingPreset = preset; // Define your exact preset values const timeOptions = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 120, 180, 240, 300, 360, 420, 480, 540, 600]; // Create and append overlay const overlay = document.createElement('div'); overlay.id = 'auto-refresh-edit-overlay'; const panel = document.createElement('div'); panel.id = 'auto-refresh-edit-panel'; // Create options HTML const optionsHTML = timeOptions.map(value => { const selected = value === preset.seconds ? 'selected' : ''; const label = value < 60 ? `${value}s` : `${Math.floor(value/60)}min`; return `<option value="${value}" ${selected}>${label}</option>`; }).join(''); panel.innerHTML = ` <h3 style="font-size: 14px; margin-top: 0;">Edit Preset</h3> <div class="auto-refresh-form-group"> <label class="auto-refresh-form-label">Select Interval:</label> <select id="edit-preset-seconds" class="auto-refresh-form-input" style="appearance: auto; background-color: white;"> ${optionsHTML} </select> <div id="edit-preset-error" style="color: #f44336; font-size: 11px; margin: 8px 0; display: none;"></div> </div> <div class="auto-refresh-form-buttons"> <button class="auto-refresh-form-button cancel">Cancel</button> <button class="auto-refresh-form-button save">Save</button> </div> `; overlay.appendChild(panel); document.body.appendChild(overlay); // Add event listeners overlay.querySelector('.cancel').addEventListener('click', hideEditPanel); overlay.querySelector('.save').addEventListener('click', saveEditedPreset); // Handle pressing Enter key const selectInput = document.getElementById('edit-preset-seconds'); selectInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); saveEditedPreset(); } }); // Focus the select field selectInput.focus(); } catch (error) { console.error('Error showing edit panel:', error); } } // Save edited preset with duplicate check function saveEditedPreset() { try { const secondsInput = document.getElementById('edit-preset-seconds'); const errorElement = document.getElementById('edit-preset-error') || (() => { // Create error element if it doesn't exist const el = document.createElement('div'); el.id = 'edit-preset-error'; el.style.cssText = 'color: #f44336; font-size: 11px; margin: 8px 0; display: none;'; secondsInput.parentNode.insertBefore(el, secondsInput.nextSibling); return el; })(); if (!secondsInput) return; const seconds = parseInt(secondsInput.value, 10); if (isNaN(seconds)) { return; } // Check if another preset already has this value const duplicatePreset = state.presets.find(p => p.seconds === seconds && !(p.name === state.editingPreset.name && p.seconds === state.editingPreset.seconds) ); if (duplicatePreset) { errorElement.textContent = `Preset "${duplicatePreset.name}" already uses this interval. Please choose another value.`; errorElement.style.display = 'block'; return; } // Auto-format name based on seconds value const name = formatPresetName(seconds); // Find and update the preset const presetIndex = state.presets.findIndex(p => p.name === state.editingPreset.name && p.seconds === state.editingPreset.seconds ); if (presetIndex >= 0) { state.presets[presetIndex] = { name, seconds }; // If this was the active preset, update it if (state.activePreset && state.activePreset.name === state.editingPreset.name && state.activePreset.seconds === state.editingPreset.seconds) { state.activePreset = { name, seconds }; if (state.enabled && state.onServiceOrdersPage) { startAutoRefresh(state.activePreset); } } // Update UI createOrUpdateUI(); // Save state saveState(); } hideEditPanel(); } catch (error) { console.error('Error saving edited preset:', error); } } // Hide preset edit panel function hideEditPanel() { try { const overlay = document.getElementById('auto-refresh-edit-overlay'); if (overlay) { overlay.remove(); } state.editingPreset = null; } catch (error) { console.error('Error hiding edit panel:', error); } } // Make element draggable - Enhanced for both mouse and touch devices function makeDraggable(element, handleElement) { try { let startX, startY, initialX, initialY; let isDragging = false; const onStart = (e) => { // Don't initiate drag if it's a button or control if (e.target.id === 'auto-refresh-collapse' || e.target.id === 'auto-refresh-toggle' || e.target.closest('button') || e.target.closest('.switch')) { return; } isDragging = true; state.isDragging = true; // Get starting positions if (e.type === 'mousedown') { startX = e.clientX; startY = e.clientY; } else if (e.type === 'touchstart') { startX = e.touches[0].clientX; startY = e.touches[0].clientY; } initialX = element.offsetLeft; initialY = element.offsetTop; // Add move and end event listeners if (e.type === 'mousedown') { document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onEnd); } else if (e.type === 'touchstart') { document.addEventListener('touchmove', onMove, { passive: false }); document.addEventListener('touchend', onEnd); } // Prevent default behavior only for the handle if (e.target === handleElement || handleElement.contains(e.target)) { if (e.preventDefault) e.preventDefault(); } }; const onMove = (e) => { if (!isDragging) return; // Calculate new position let clientX, clientY; if (e.type === 'mousemove') { clientX = e.clientX; clientY = e.clientY; } else if (e.type === 'touchmove') { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; e.preventDefault(); // Prevent scrolling when dragging } // Calculate new position const deltaX = clientX - startX; const deltaY = clientY - startY; const newLeft = initialX + deltaX; const newTop = initialY + deltaY; // Ensure the element stays within viewport bounds const maxTop = window.innerHeight - element.offsetHeight; const maxLeft = window.innerWidth - element.offsetWidth; element.style.top = `${Math.min(Math.max(0, newTop), maxTop)}px`; element.style.left = `${Math.min(Math.max(0, newLeft), maxLeft)}px`; }; const onEnd = (e) => { isDragging = false; // Wait a short moment before setting isDragging to false to prevent accidental clicks setTimeout(() => { state.isDragging = false; }, 50); // Remove move and end event listeners document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onEnd); document.removeEventListener('touchmove', onMove); document.removeEventListener('touchend', onEnd); // Save the new position state.position = { x: parseInt(element.style.left, 10) || 20, y: parseInt(element.style.top, 10) || 20 }; saveState(); }; handleElement.addEventListener('mousedown', onStart); handleElement.addEventListener('touchstart', onStart, { passive: true }); } catch (error) { console.error('Error making element draggable:', error); } } // Create or update UI function createOrUpdateUI() { try { // Check if UI already exists let container = document.getElementById('auto-refresh-container'); if (!container) { // Create container container = document.createElement('div'); container.id = 'auto-refresh-container'; document.body.appendChild(container); // Set initial position container.style.left = `${state.position.x}px`; container.style.top = `${state.position.y}px`; // Create UI structure - Updated for more compact layout with better spacing container.innerHTML = ` <div id="auto-refresh-header"> <span id="auto-refresh-title">Auto Refresh</span> <div id="auto-refresh-controls"> <label class="switch"> <input type="checkbox" id="auto-refresh-toggle" ${state.enabled ? 'checked' : ''}> <span class="slider"></span> </label> <span id="auto-refresh-collapse">${state.collapsed ? '+' : '-'}</span> </div> </div> <div id="auto-refresh-body" style="max-height: ${state.collapsed ? '0' : '500px'};"> <div id="auto-refresh-presets"></div> <div id="auto-refresh-status" class="info">Initializing...</div> </div> `; // Add event listeners document.getElementById('auto-refresh-toggle').addEventListener('change', toggleEnabled); // IMPORTANT FIX: Add event listener directly without cloning const collapseBtn = document.getElementById('auto-refresh-collapse'); if (collapseBtn) { // Remove previous listeners if any (just in case) const newCollapseBtn = collapseBtn.cloneNode(true); if (collapseBtn.parentNode) { collapseBtn.parentNode.replaceChild(newCollapseBtn, collapseBtn); } // Get fresh reference to the button that's now in the DOM const domCollapseBtn = document.getElementById('auto-refresh-collapse'); if (domCollapseBtn) { // Use normal click handler - simpler is better domCollapseBtn.onclick = function(e) { // Stop propagation to prevent draggable from interfering if (e) { e.stopPropagation(); e.preventDefault(); } toggleCollapsed(); return false; }; } } // Make draggable - with special handling for the collapse button makeDraggable(container, document.getElementById('auto-refresh-header')); // If disabled, add class if (!state.enabled) { container.classList.add('auto-refresh-disabled'); } } // Update presets const presetsContainer = document.getElementById('auto-refresh-presets'); if (presetsContainer) { presetsContainer.innerHTML = ''; state.presets.forEach(preset => { const presetBtn = document.createElement('button'); presetBtn.className = 'auto-refresh-preset'; presetBtn.textContent = preset.name; presetBtn.dataset.seconds = preset.seconds; // If this is the active preset, add active class if (state.activePreset && state.activePreset.name === preset.name && state.activePreset.seconds === preset.seconds) { presetBtn.classList.add('active'); } // Add click event presetBtn.addEventListener('click', (e) => { // Only process click if we're not in a drag operation if (!state.isDragging && state.enabled) { startAutoRefresh(preset); } }); // Add context menu / long press presetBtn.addEventListener('contextmenu', (e) => { e.preventDefault(); showEditPanel(preset); }); // Long press detection for touch devices let longPressTimer; let longPressStarted = false; presetBtn.addEventListener('touchstart', (e) => { longPressStarted = true; longPressTimer = setTimeout(() => { if (longPressStarted) { showEditPanel(preset); } }, 800); }); presetBtn.addEventListener('touchmove', () => { // Cancel long press if user moves finger longPressStarted = false; clearTimeout(longPressTimer); }); presetBtn.addEventListener('touchend', () => { // Only process click if we're not in a drag operation and not a long press if (!state.isDragging && longPressStarted && state.enabled) { startAutoRefresh(preset); } longPressStarted = false; clearTimeout(longPressTimer); }); presetsContainer.appendChild(presetBtn); }); } // Update status based on current state - Simplified status messages updatePageStatus(); } catch (error) { console.error('Error creating or updating UI:', error); } } // Update page status based on current state function updatePageStatus() { if (!state.onServiceOrdersPage) { updateStatus('warning', 'Please navigate to Service Orders page'); } else if (!state.enabled) { updateStatus('info', 'Auto refresh is off'); } else if (state.lastRefreshTime) { updateStatus('success', `Last checked: ${formatTime(state.lastRefreshTime)}`); } else { updateStatus('info', 'Select a refresh interval'); } } // Check page and update UI accordingly function checkPageAndUpdateUI() { try { const wasOnServiceOrdersPage = state.onServiceOrdersPage; state.onServiceOrdersPage = checkIfOnServiceOrdersPage(); // Reset refresh button observed state if we're not on the service orders page if (!state.onServiceOrdersPage) { state.refreshButtonObserved = false; } // Update status based on current state updatePageStatus(); // If we just arrived at the service orders page if (!wasOnServiceOrdersPage && state.onServiceOrdersPage) { // Trigger an immediate refresh if (state.enabled) { triggerRefresh(); // If we have an active preset, start auto refresh if (state.activePreset) { startAutoRefresh(state.activePreset); } } } else if (wasOnServiceOrdersPage && !state.onServiceOrdersPage) { // If we've left the service orders page clearAutoRefresh(); } } catch (error) { console.error('Error checking page and updating UI:', error); } } // Setup observer function setupObserver() { try { const observer = new MutationObserver(mutations => { // Only check once every second at most to avoid performance issues if (!state.observerDebounce) { state.observerDebounce = true; setTimeout(() => { checkPageAndUpdateUI(); state.observerDebounce = false; }, 1000); } }); // Start observing observer.observe(document.body, { childList: true, subtree: true }); } catch (error) { console.error('Error setting up observer:', error); } } // Initialize the script function init() { try { // Load saved state loadState(); // Create initial UI createOrUpdateUI(); // Setup observer to detect page changes setupObserver(); // Check if we're on service orders page initially checkPageAndUpdateUI(); // If we have an active preset and are on service orders page, start auto refresh if (state.activePreset && state.onServiceOrdersPage && state.enabled) { startAutoRefresh(state.activePreset); } state.initialized = true; } catch (error) { console.error('Error initializing Auto Refresh Tool:', error); } } // Handle errors globally to prevent leakage to user window.addEventListener('error', function(event) { // Check if error is from our script if (event.filename && event.filename.includes('Auto Refresh Tool')) { console.error('Auto Refresh Tool error:', event.error); event.preventDefault(); return true; } return false; }, true); // Try multiple initialization attempts let initAttempts = 0; const maxInitAttempts = 3; function attemptInit() { if (initAttempts >= maxInitAttempts) { console.error('Failed to initialize Auto Refresh Tool after multiple attempts'); return; } if (!state.initialized) { initAttempts++; init(); // Schedule another attempt if not successful if (!state.initialized) { setTimeout(attemptInit, 2000); } } } // Start the initialization process with a slight delay setTimeout(attemptInit, 1000); // Backup initialization - forcefully create UI after page has likely loaded setTimeout(() => { if (!document.getElementById('auto-refresh-container')) { createOrUpdateUI(); checkPageAndUpdateUI(); } }, 5000); })();