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