// ==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);
})();