KAAUH Lab Enhancement Suite 1 (Buttons, Alerts, Auto-Actions) - Modified Alert Handling

Combines verification buttons (F7/F8), dynamic alerts (>, NO RESULT, X-NORESULT, CL/CH) showing only the *last* alert per scan, checkbox automation, toggle back-nav, and improved scrolling.

当前为 2025-04-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         KAAUH Lab Enhancement Suite 1 (Buttons, Alerts, Auto-Actions) - Modified Alert Handling
// @namespace    Violentmonkey Scripts
// @version      5.6.4
// @description  Combines verification buttons (F7/F8), dynamic alerts (>, NO RESULT, X-NORESULT, CL/CH) showing only the *last* alert per scan, checkbox automation, toggle back-nav, and improved scrolling.
// @match        *://his.kaauh.org/lab/*
// @grant        none
// @author       Hamad AlShegifi
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // --- Configuration ---
  const CONFIG = {
    // General & Debugging
    DEBUG_MODE: true,
    PERSISTENT_MODALS: true, // Modals require manual dismissal
    NO_RESULT_MESSAGE: "NO-RESULT DETECTED!!", // Standardized message

    // Script 1: Buttons & Verification
    TARGET_EDIT_PAGE_URL_PREFIX: 'https://his.kaauh.org/lab/#/lab-orders/edit-lab-order/',
    EXCLUDE_WORDS: ['culture', "gram's stain", 'stain', 'bacterial', 'fungal', 'culture', 'pcr', 'Meningitis', 'MRSA', 'Mid', 'stream', 'Cryptococcus'],
    VERIFY1_BUTTON_SELECTOR: '#custom-script-buttons button.btn-success',
    VERIFY2_BUTTON_SELECTOR: '#custom-script-buttons button.btn-primary',
    COMPLETE_TECH_VERIFY_SELECTOR: 'button.dropdown-item[translateid="test-results.CompleteTechnicalVerification"]',
    COMPLETE_MED_VERIFY_SELECTOR: 'button.dropdown-item[translateid="test-results.CompleteMedicalVerification"]',
    FINAL_VERIFY_BUTTON_SELECTOR: 'button.btn-success.btn-sm.min-width[translateid="test-verification.Verify"]',
    NEXT_BUTTON_SELECTOR: 'button#btnNext',
    TEST_DESC_SELECTOR: 'div[col-id="TestDesc"]',
    UNCHECKED_BOX_SELECTOR: 'span.ag-icon-checkbox-unchecked[unselectable="on"]',
    CHECKBOX_PARENT_ROW_SELECTOR: '.ag-row',

    // Script 2: Alerts & Scanning
    SCAN_INTERVAL: 150,
    FLASH_COLOR: "pink",
    FLASH_INTERVAL: 500,
    // MODAL_TIMEOUT: 10000, // Removed as PERSISTENT_MODALS=true makes it irrelevant
    RESULT_CELL_SELECTOR: 'div[role="gridcell"][col-id="TestResult"] app-result-value-render div',
    CRITICAL_FLAG_SELECTOR: 'div[role="gridcell"][col-id="LTFlag"] app-ref-high-low div span.critial-alret-indication',

    // Script 3 & General Toast Handling
    UNDEFINED_URL_CHECK_INTERVAL: 200,
    TOAST_CONTAINER_SELECTOR: '#toast-container',
    TOAST_CLOSE_BUTTON_SELECTOR: 'button.toast-close-button',
    SUCCESS_TOAST_SELECTOR: '.toast-success',
  };

  // --- State Variables ---
  let verify1Toggle = localStorage.getItem('verify1Toggle') === 'true';
  let verify2Toggle = localStorage.getItem('verify2Toggle') === 'true';
  let verify1Clicked = false;
  let verify2Clicked = false;
  let issueScanIntervalId = null;
  let isScanningActive = false;
  const activeModals = new Set();

  // --- Utility Functions ---
  function logDebug(message) {
    // Added check for console availability
    if (CONFIG.DEBUG_MODE && typeof console !== 'undefined' && console.log) {
        console.log(`[Lab Suite v5.6.4] ${message}`);
    }
  }

  function loadFontAwesome() {
    const existingLink = document.querySelector('link[href*="font-awesome"]');
    if (!existingLink) {
      const link = document.createElement('link');
      link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
      link.rel = 'stylesheet';
      document.head.appendChild(link);
      logDebug('Font Awesome loaded');
    }
  }

  function isVisible(element) {
    return !!(element && (element.offsetWidth || element.offsetHeight || element.getClientRects().length));
  }

  // --- Modal Functions ---
  function showModal(message) {
    // Remove duplicate modals with same message - prevents spam if scan is too fast
    document.querySelectorAll('.lab-suite-modal').forEach(modal => {
      const contentElement = modal.querySelector('p'); // Target the content paragraph
      if (contentElement && contentElement.textContent === message) {
        const overlay = document.querySelector('.lab-suite-modal-overlay[data-modal-ref="' + modal.id + '"]');
        logDebug(`Removing duplicate modal for message: ${message}`);
        if (overlay) overlay.remove();
        modal.remove();
        activeModals.delete(modal); // Ensure removal from active set
      }
    });

    logDebug(`Showing modal: ${message}`);
    const modalId = `lab-suite-modal-${Date.now()}-${Math.random().toString(16).slice(2)}`; // Unique ID

    const overlay = document.createElement("div");
    overlay.className = 'lab-suite-modal-overlay';
    overlay.style.position = "fixed";
    overlay.style.top = "0";
    overlay.style.left = "0";
    overlay.style.width = "100%";
    overlay.style.height = "100%";
    overlay.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
    overlay.style.zIndex = "2000";
    overlay.style.opacity = "0";
    overlay.style.transition = "opacity 0.3s ease";
    overlay.setAttribute('data-modal-ref', modalId); // Link overlay to modal
    document.body.appendChild(overlay);

    const modal = document.createElement("div");
    modal.id = modalId; // Assign unique ID
    modal.className = 'lab-suite-modal';
    modal.style.position = "fixed";
    modal.style.top = "50%";
    modal.style.left = "50%";
    modal.style.transform = "translate(-50%, -50%) scale(0.5)";
    modal.style.backgroundColor = "#f4f4f9";
    modal.style.padding = "30px";
    modal.style.boxShadow = "0px 10px 30px rgba(0, 0, 0, 0.15)";
    modal.style.zIndex = "2001";
    modal.style.borderRadius = "15px";
    modal.style.textAlign = "center";
    modal.style.transition = "transform 0.3s ease, opacity 0.3s ease";
    modal.style.opacity = "0";
    modal.style.maxWidth = "450px";
    modal.setAttribute('role', 'alertdialog'); // More specific role
    modal.setAttribute('aria-labelledby', `${modalId}-heading`);
    modal.setAttribute('aria-describedby', `${modalId}-content`);
    modal.setAttribute('aria-modal', 'true');

    const heading = document.createElement("h2");
    heading.id = `${modalId}-heading`; // ID for aria-labelledby
    heading.textContent = "Attention!";
    heading.style.fontFamily = "'Arial', sans-serif";
    heading.style.color = "#333";
    heading.style.marginBottom = "10px";
    heading.style.fontSize = "24px";
    modal.appendChild(heading);

    const content = document.createElement("p");
    content.id = `${modalId}-content`; // ID for aria-describedby
    // Standardize NO RESULT messages
    let displayMessage = message;
    if (message.includes("NO RESULT") || message.includes("X-NORESULT")) {
      displayMessage = CONFIG.NO_RESULT_MESSAGE;
    }
    content.textContent = displayMessage;
    content.style.fontFamily = "'Arial', sans-serif";
    content.style.color = "#555";
    content.style.marginBottom = "20px";
    content.style.fontSize = "16px";
    content.style.lineHeight = "1.5";
    modal.appendChild(content);

    const okButton = createModalButton("OK", "#ff4081", () => {
      logDebug(`Modal [${modalId}] manually dismissed.`);
      closeModal(modal, overlay);
    });
    modal.appendChild(okButton);

    document.body.appendChild(modal);
    okButton.focus(); // Focus the OK button for accessibility

    // Animate appearance
    setTimeout(() => {
      overlay.style.opacity = "1";
      modal.style.transform = "translate(-50%, -50%) scale(1)";
      modal.style.opacity = "1";
    }, 10);

    activeModals.add(modal);

    // Auto-close functionality (if PERSISTENT_MODALS is false) - currently disabled by config
    // if (!CONFIG.PERSISTENT_MODALS && CONFIG.MODAL_TIMEOUT > 0) {
    //   setTimeout(() => {
    //     logDebug(`Modal [${modalId}] automatically closing after timeout.`);
    //     closeModal(modal, overlay);
    //   }, CONFIG.MODAL_TIMEOUT);
    // }
  }

  function createModalButton(text, backgroundColor, onClick) {
    const button = document.createElement("button");
    button.textContent = text;
    button.style.padding = "10px 20px";
    button.style.border = "none";
    button.style.backgroundColor = backgroundColor;
    button.style.color = "white";
    button.style.borderRadius = "30px";
    button.style.cursor = "pointer";
    button.style.fontSize = "16px";
    button.style.transition = "background-color 0.3s ease, transform 0.2s ease";
    button.style.minWidth = '80px';
    button.style.margin = '0 5px';

    button.addEventListener("mouseenter", () => {
      button.style.backgroundColor = darkenColor(backgroundColor, 20);
      button.style.transform = "scale(1.05)";
    });
    button.addEventListener("mouseleave", () => {
      button.style.backgroundColor = backgroundColor;
      button.style.transform = "scale(1)";
    });
    button.addEventListener("click", onClick);
    return button;
  }

  function closeModal(modal, overlay) {
    if (!modal || !overlay || !document.body.contains(modal)) {
        logDebug("Attempted to close a modal that doesn't exist or is already removed.");
        return;
    }
    logDebug(`Closing modal [${modal.id}].`);
    modal.style.transform = "translate(-50%, -50%) scale(0.5)";
    modal.style.opacity = "0";
    overlay.style.opacity = "0";

    // Remove after transition
    setTimeout(() => {
      if (document.body.contains(modal)) {
          document.body.removeChild(modal);
          activeModals.delete(modal); // Ensure removal from active set
          logDebug(`Modal [${modal.id}] removed from DOM.`);
      } else {
          logDebug(`Modal [${modal.id}] was already removed from DOM before timeout.`);
      }
      if (document.body.contains(overlay)) {
           document.body.removeChild(overlay);
           logDebug(`Overlay for modal [${modal.id}] removed from DOM.`);
      } else {
          logDebug(`Overlay for modal [${modal.id}] was already removed from DOM before timeout.`);
      }
    }, 300); // Corresponds to transition duration
  }

  function darkenColor(color, percent) {
    try {
      // Ensure color starts with #
      if (!color.startsWith('#')) {
          logDebug(`Invalid color format for darkenColor: ${color}`);
          return color; // Return original if format is wrong
      }
      let num = parseInt(color.slice(1), 16);
      let amt = Math.round(2.55 * percent);
      let R = (num >> 16) - amt;
      let G = ((num >> 8) & 0x00ff) - amt;
      let B = (num & 0x0000ff) - amt;
      R = R < 0 ? 0 : R;
      G = G < 0 ? 0 : G;
      B = B < 0 ? 0 : B;
      return `#${(0x1000000 + R * 0x10000 + G * 0x100 + B).toString(16).slice(1)}`;
    } catch (e) {
      logDebug(`Error darkening color ${color}: ${e}`);
      return color; // Return original on error
    }
  }

  // --- Script 1 Functions ---
  function isCorrectPage() {
    return window.location.href.startsWith(CONFIG.TARGET_EDIT_PAGE_URL_PREFIX);
  }

  function createVerifyButton(label, className, onClick) {
    let button = document.createElement('button');
    button.type = 'button';
    button.innerHTML = label;
    button.className = className;

    let buttonColors = {
      'btn btn-success btn-sm': '#28a745', // Green for VERIFY1
      'btn btn-primary btn-sm': '#2594d9'  // Blue for VERIFY2
    };

    // Basic styles - !important might be needed depending on site CSS specificity
    button.style.cssText = `
        font-family: 'Arial', sans-serif !important;
        font-size: 14px !important;
        font-weight: normal !important;
        color: #ffffff !important;
        background-color: ${buttonColors[className] || '#6c757d'} !important; /* Default grey */
        padding: 8px 16px !important;
        border: none !important;
        border-radius: 5px !important;
        text-shadow: none !important;
        cursor: pointer !important;
        margin-right: 5px !important;
        line-height: 1.5 !important;
        vertical-align: middle; /* Align with icons */
      `;
    button.onclick = onClick;
    return button;
  }

 function createToggleIcon(id, isActive, onClick) {
    let icon = document.createElement('span');
    icon.id = id;
    // Using Font Awesome classes directly in innerHTML
    icon.innerHTML = `<i class="fas fa-arrow-circle-left" aria-hidden="true" style="color: ${isActive ? '#008000' : '#d1cfcf'}; font-size: 1.3em; vertical-align: middle;"></i>`;
    icon.style.cursor = 'pointer';
    icon.style.marginRight = '10px'; // Space after icon
    icon.style.marginLeft = '-1px';  // Adjust spacing relative to button
    icon.onclick = onClick;
    icon.title = "Toggle: Go back automatically after this verification?"; // More descriptive title
    return icon;
 }

  function handleVerify1IconToggle() {
    verify1Toggle = !verify1Toggle;
    localStorage.setItem('verify1Toggle', verify1Toggle);
    const iconElement = document.querySelector('#verify1Icon i'); // Target the <i> tag
    if (iconElement) iconElement.style.color = verify1Toggle ? '#008000' : '#d1cfcf'; // Green/Grey
    logDebug(`Verify1 Auto-Back Toggle set to: ${verify1Toggle}`);
  }

  function handleVerify2IconToggle() {
    verify2Toggle = !verify2Toggle;
    localStorage.setItem('verify2Toggle', verify2Toggle);
    const iconElement = document.querySelector('#verify2Icon i'); // Target the <i> tag
    if (iconElement) iconElement.style.color = verify2Toggle ? '#008000' : '#d1cfcf'; // Green/Grey
    logDebug(`Verify2 Auto-Back Toggle set to: ${verify2Toggle}`);
  }

  function addButtons() {
    if (document.getElementById('custom-script-buttons') || !isCorrectPage()) return;

    const nextButton = document.querySelector(CONFIG.NEXT_BUTTON_SELECTOR);
    if (nextButton && nextButton.parentNode) {
      logDebug("Adding custom VERIFY buttons.");
      let buttonDiv = document.createElement('div');
      buttonDiv.id = 'custom-script-buttons';
      buttonDiv.style.display = 'inline-block'; // Keep elements inline
      buttonDiv.style.marginLeft = '10px'; // Space from Next button
      buttonDiv.style.verticalAlign = 'middle'; // Align container vertically

      // VERIFY1 Button & Toggle
      let verify1Button = createVerifyButton('VERIFY1 (F7)', 'btn btn-success btn-sm', () => {
        logDebug("VERIFY1 button clicked.");
        verify1Clicked = true;
        verify2Clicked = false;
        checkAllVisibleBoxesWithoutDuplicates();
        // Add slight delay before clicking dropdown item
        setTimeout(() => { clickCompleteTechnicalVerificationButton(); }, 500);
      });
      let verify1Icon = createToggleIcon('verify1Icon', verify1Toggle, handleVerify1IconToggle);

      // VERIFY2 Button & Toggle
      let verify2Button = createVerifyButton('VERIFY2 (F8)', 'btn btn-primary btn-sm', () => {
        logDebug("VERIFY2 button clicked.");
        verify2Clicked = true;
        verify1Clicked = false;
        checkAllVisibleBoxesWithoutDuplicates();
        // Add slight delay before clicking dropdown item
        setTimeout(() => { clickCompleteMedicalVerificationButton(); }, 500);
      });
      let verify2Icon = createToggleIcon('verify2Icon', verify2Toggle, handleVerify2IconToggle);

      // Author Credit (Styled)
      let modedByText = document.createElement('span');
      modedByText.textContent = "Modded by: Hamad AlShegifi";
      modedByText.style.fontSize = '11px'; // Slightly smaller
      modedByText.style.fontWeight = 'bold';
      modedByText.style.color = '#e60000'; // Darker red
      modedByText.style.marginLeft = '15px'; // Space before credit
      modedByText.style.border = '1px solid #e60000';
      modedByText.style.borderRadius = '5px'; // Matched button radius
      modedByText.style.padding = '3px 6px';
      modedByText.style.backgroundColor = '#fff0f0'; // Very light pink background
      modedByText.style.verticalAlign = 'middle'; // Align with buttons/icons

      // Append elements in order
      buttonDiv.appendChild(verify1Button);
      buttonDiv.appendChild(verify1Icon);
      buttonDiv.appendChild(verify2Button);
      buttonDiv.appendChild(verify2Icon);
      buttonDiv.appendChild(modedByText); // Add credit last

      // Insert the div after the Next button
      nextButton.parentNode.insertBefore(buttonDiv, nextButton.nextSibling);
    } else {
      logDebug("Could not find Next button ('" + CONFIG.NEXT_BUTTON_SELECTOR + "') to anchor custom buttons.");
      // Fallback: Append to body or a known container if Next button is missing?
      // For now, it just logs the failure.
    }
  }

  function checkAllVisibleBoxesWithoutDuplicates() {
    logDebug("Checking checkboxes...");
    const testDivs = document.querySelectorAll(CONFIG.TEST_DESC_SELECTOR);
    let seenTests = new Set();
    let boxesChecked = 0;
    // Ensure exclude words are lowercase for comparison
    const excludeWordsLower = CONFIG.EXCLUDE_WORDS.map(word => word.toLowerCase());

    testDivs.forEach(testDiv => {
      const testName = testDiv.textContent?.trim().toLowerCase() || '';
      if (!testName) return; // Skip empty descriptions

      // Check if test name contains any excluded word
      if (excludeWordsLower.some(word => testName.includes(word))) {
        logDebug(`Excluding checkbox for test containing excluded word: ${testName}`);
        return; // Skip this test
      }

      // Check for duplicates (only check the first instance of a test name)
      if (seenTests.has(testName)) {
        logDebug(`Skipping duplicate test: ${testName}`);
        return; // Skip this duplicate test
      }

      seenTests.add(testName); // Add unique test name to set

      // Find the parent row and the unchecked checkbox within it
      const parentRow = testDiv.closest(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR);
      if (parentRow) {
        const checkbox = parentRow.querySelector(CONFIG.UNCHECKED_BOX_SELECTOR);
        // Ensure checkbox exists and is visible before clicking
        if (checkbox && isVisible(checkbox)) {
          logDebug(`Clicking checkbox for unique, non-excluded test: ${testName}`);
          // Simulate a user click more reliably
          const event = new MouseEvent('click', { bubbles: true, cancelable: true, view: window });
          checkbox.dispatchEvent(event);
          boxesChecked++;
        } else if (checkbox && !isVisible(checkbox)) {
            logDebug(`Checkbox found but not visible for test: ${testName}`);
        }
      } else {
          logDebug(`Could not find parent row for test: ${testName}`);
      }
    });
    logDebug(`${boxesChecked} unique, non-excluded, visible checkboxes were checked.`);
  }

  function clickCompleteTechnicalVerificationButton() {
    const button = document.querySelector(CONFIG.COMPLETE_TECH_VERIFY_SELECTOR);
    if (button) {
      button.click();
      logDebug("Complete Technical Verification button clicked!");
      // Wait slightly longer for the final verify button to appear/enable
      setTimeout(() => { clickFinalVerifyButton(); }, 700);
    } else {
      logDebug("Complete Technical Verification button not found!");
      showModal("Error: Unable to find the 'Complete Technical Verification' button.");
    }
  }

  function clickCompleteMedicalVerificationButton() {
    const button = document.querySelector(CONFIG.COMPLETE_MED_VERIFY_SELECTOR);
    if (button) {
      button.click();
      logDebug("Complete Medical Verification button clicked!");
      // Wait slightly longer for the final verify button to appear/enable
      setTimeout(() => { clickFinalVerifyButton(); }, 700);
    } else {
      logDebug("Complete Medical Verification button not found!");
      showModal("Error: Unable to find the 'Complete Medical Verification' button.");
    }
  }

  function clickFinalVerifyButton() {
    const verifyButton = document.querySelector(CONFIG.FINAL_VERIFY_BUTTON_SELECTOR);
    if (verifyButton && !verifyButton.disabled) { // Check if button exists and is not disabled
      verifyButton.click();
      logDebug("Final Verify button clicked!");
      // Reset click flags AFTER successful final verify click
      // verify1Clicked = false; // Moved reset logic to toast observer/click
      // verify2Clicked = false;
    } else if (verifyButton && verifyButton.disabled) {
        logDebug("Final Verify button found, but it is disabled.");
        showModal("Error: The final 'Verify' button is disabled. Cannot proceed.");
    } else {
      logDebug("Final Verify button not found!");
      showModal("Error: Unable to find the final 'Verify' button.");
    }
  }

 // --- Script 2 Functions ---
 function applyFlashingEffect(rows) {
   rows.forEach(row => {
     // Prevent multiple intervals on the same row
     if (row.dataset.flashing === 'true') {
        // logDebug("Flashing already active on this row.");
        return;
     }
     row.dataset.flashing = 'true'; // Mark as flashing
     logDebug(`Applying flashing effect to row ID: ${row.getAttribute('row-id') || 'N/A'}`);

     // Store original background for restoring later if needed, default to transparent
     const originalBg = row.style.backgroundColor || 'transparent';
     row.dataset.originalBg = originalBg; // Store it

     row.style.transition = "background-color 0.5s ease";
     let isPink = false;

     const intervalId = setInterval(() => {
       // Check if row still exists in DOM
       if (!document.body.contains(row)) {
         clearInterval(intervalId);
         // No need to reset background if element is gone
         logDebug(`Row ${row.getAttribute('row-id') || 'N/A'} removed, stopping its flash interval.`);
         return;
       }
       // Check if flashing should stop (e.g., modal dismissed or issue resolved - needs external trigger)
       if (row.dataset.flashing === 'false') {
           clearInterval(intervalId);
           row.style.backgroundColor = row.dataset.originalBg || 'transparent'; // Restore original
           logDebug(`Flashing stopped externally for row ID: ${row.getAttribute('row-id') || 'N/A'}. Restored background.`);
           delete row.dataset.flashing; // Clean up attribute
           delete row.dataset.originalBg;
           delete row.dataset.flashIntervalId;
           return;
       }

       isPink = !isPink;
       row.style.backgroundColor = isPink ? CONFIG.FLASH_COLOR : originalBg;
     }, CONFIG.FLASH_INTERVAL);

     // Store interval ID on the row itself for potential external clearing
     row.dataset.flashIntervalId = intervalId;
   });
 }

 function stopFlashingEffect(row) {
     if (row && row.dataset.flashing === 'true') {
         logDebug(`Requesting to stop flashing for row ID: ${row.getAttribute('row-id') || 'N/A'}`);
         row.dataset.flashing = 'false'; // Signal interval to stop and restore color
         // The interval itself handles cleanup
     }
 }


 function getNotificationSessionKey(type, identifier = 'general') {
    // Include pathname to make keys specific to the current view/order
    return `labSuiteNotified_${window.location.pathname}_${type}_${identifier}`;
 }

 function hasAlreadyNotified(type, identifier = 'general') {
    const key = getNotificationSessionKey(type, identifier);
    const notified = sessionStorage.getItem(key) === 'true';
    // if (notified) logDebug(`Notification flag FOUND for key: ${key}`);
    return notified;
 }

 function setNotificationFlag(type, identifier = 'general') {
    const key = getNotificationSessionKey(type, identifier);
    logDebug(`Setting notification flag for key: ${key}`);
    sessionStorage.setItem(key, 'true');
 }

// ==================================================
// == MODIFIED checkForIssues Function Starts Here ==
// ==================================================
 function checkForIssues() {
   const resultDivs = document.querySelectorAll(CONFIG.RESULT_CELL_SELECTOR);
   const criticalDivs = document.querySelectorAll(CONFIG.CRITICAL_FLAG_SELECTOR);
   const potentialAlerts = []; // Store potential alerts found in this scan

   // Helper function to scroll with 'nearest' block alignment
   function scrollToRowNearest(element) {
     const row = element.closest(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR);
     if (row) {
       const rowId = row.getAttribute('row-id');
       logDebug(`Scrolling to nearest position for row ID: ${rowId || 'ID not found'}`);
       // Use 'nearest' to minimize scrolling distance and avoid centering
       try {
           row.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
       } catch (e) {
            logDebug(`Error during scrollIntoView: ${e}. Falling back to basic scroll.`);
            row.scrollIntoView(); // Fallback
       }
       return true;
     }
     logDebug('Could not find parent row to scroll to.');
     return false;
   }

   // 1. Check for Critical Flags (CL/CH)
   criticalDivs.forEach(div => {
     const text = div.textContent?.trim() || '';
     if (text === "CL" || text === "CH") {
       const rowElement = div.closest(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR);
       if (rowElement) {
          const specificRowKey = rowElement.getAttribute('row-id') || text + "_" + Array.from(criticalDivs).indexOf(div); // Use row-id or text+index as fallback
           if (!hasAlreadyNotified('critical', specificRowKey)) {
               const message = text === "CL" ? "CRITICAL LOW RESULT DETECTED !!" : "CRITICAL HIGH RESULT DETECTED !!";
               logDebug(`Found potential critical alert (${text}) for row ${specificRowKey}`);
               potentialAlerts.push({
                   element: div, // Store the element that triggered the alert
                   rowElement: rowElement, // Store the row itself for easier scrolling later
                   type: 'critical',
                   message: message,
                   rowId: specificRowKey
               });
           } else {
                // logDebug(`Critical alert (${text}) for row ${specificRowKey} already notified this session.`);
           }
       }
     }
   });

   // 2. Check for "NO RESULT" / "X-NORESULT"
   resultDivs.forEach(div => {
     const text = div.textContent?.trim().toLowerCase() || '';
     const isNoResultType = (text === "no result" || text === "no-xresult" || text === "x-noresult");

     if (isNoResultType) {
        const rowElement = div.closest(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR);
        if (rowElement) {
            const specificRowKey = rowElement.getAttribute('row-id') || text.replace(/[\s-]+/g, '_') + "_" + Array.from(resultDivs).indexOf(div); // Use row-id or text+index
            if (!hasAlreadyNotified('noresult', specificRowKey)) {
                logDebug(`Found potential noresult alert (${text}) for row ${specificRowKey}`);
                potentialAlerts.push({
                    element: div,
                    rowElement: rowElement,
                    type: 'noresult',
                    message: CONFIG.NO_RESULT_MESSAGE, // Use standardized message
                    rowId: specificRowKey
                });
            } else {
                // logDebug(`Noresult alert (${text}) for row ${specificRowKey} already notified this session.`);
            }
        }
     }
   });

   // 3. Check for ">" (Dilution Required)
   resultDivs.forEach(div => {
     const text = div.textContent?.trim() || ''; // Don't lowercase here, '>' is specific
     if (text.includes(">")) {
       const rowElement = div.closest(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR);
       if (rowElement) {
           const rowId = rowElement.getAttribute('row-id') || ">_" + Array.from(resultDivs).indexOf(div); // Use row-id or symbol+index
           if (!hasAlreadyNotified('greaterThan', rowId)) {
                logDebug(`Found potential greaterThan alert (>) for row ${rowId}`);
                // Apply flashing immediately for any '>' found, regardless of whether it's the final alert
                applyFlashingEffect([rowElement]); // Pass row element in array
                potentialAlerts.push({
                   element: div,
                   rowElement: rowElement,
                   type: 'greaterThan',
                   message: "Dilution is required for this sample (> detected)!",
                   rowId: rowId
               });
           } else {
                // Apply flashing even if notified, as the condition persists
                applyFlashingEffect([rowElement]);
                // logDebug(`GreaterThan alert (>) for row ${rowId} already notified this session, but applying flash.`);
           }
       }
     }
   });

   // --- Process the collected alerts ---
   if (potentialAlerts.length > 0) {
     // Get the LAST alert found during the scan (assumes DOM order is stable)
     const lastAlert = potentialAlerts[potentialAlerts.length - 1];
     logDebug(`Potential alerts found: ${potentialAlerts.length}. Selecting the last one: Type=${lastAlert.type}, RowID=${lastAlert.rowId}`);

     // Check if *this specific last alert* has already been notified and dismissed in this session
     if (!hasAlreadyNotified(lastAlert.type, lastAlert.rowId)) {
         // Show the modal for the last alert found
         showModal(lastAlert.message);

         // Set the notification flag for this specific alert row/type
         // This prevents this exact alert from showing again until the session ends or changes path
         setNotificationFlag(lastAlert.type, lastAlert.rowId);

         // Scroll to the row of the last alert using 'nearest'
         scrollToRowNearest(lastAlert.element);

         return true; // Indicate that an alert was processed in this cycle
     } else {
         logDebug(`Last potential alert (Type=${lastAlert.type}, RowID=${lastAlert.rowId}) was already notified in this session. Modal suppressed.`);
         // Ensure flashing is still applied if it's a '>' type, even if notified
         if (lastAlert.type === 'greaterThan' && lastAlert.rowElement) {
             applyFlashingEffect([lastAlert.rowElement]);
         }
     }
   } else {
       // logDebug("No new alert conditions found in this scan cycle.");
   }

   return false; // No new, un-notified alert was processed in this cycle
 }
// ================================================
// == MODIFIED checkForIssues Function Ends Here ==
// ================================================


 function startContinuousScanning() {
   if (isScanningActive) {
       // logDebug("Scanning already active.");
       return;
   }
   logDebug("Starting continuous issue scanning...");
   isScanningActive = true;

   // Clear any previous interval just in case
   if (issueScanIntervalId) clearInterval(issueScanIntervalId);

   issueScanIntervalId = setInterval(() => {
     // Check if the grid elements we monitor still exist
     const resultsGridExists = document.querySelector(CONFIG.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG.CRITICAL_FLAG_SELECTOR);
     if (!resultsGridExists && isScanningActive) { // Add isScanningActive check to prevent race condition log
       logDebug("Monitored result/critical elements disappeared, stopping issue scan.");
       stopContinuousScanning(); // Stop if grid is gone
       return;
     }
     // Run the check
     checkForIssues();
   }, CONFIG.SCAN_INTERVAL);
 }

 function stopContinuousScanning() {
   if (issueScanIntervalId) {
     clearInterval(issueScanIntervalId);
     issueScanIntervalId = null;
     logDebug("Stopped continuous issue scanning.");
   }
   // Reset flashing state for all rows when scanning stops (e.g., page navigation)
   document.querySelectorAll(CONFIG.CHECKBOX_PARENT_ROW_SELECTOR + '[data-flashing="true"]').forEach(row => {
       stopFlashingEffect(row);
   });
   isScanningActive = false;
 }

 // --- Script 3 Function ---
 function checkUrlAndTriggerClickForUndefined() {
   // Avoid running if modals are open, as it might dismiss them unexpectedly
    if (activeModals.size > 0) {
        return;
    }

   const currentUrl = window.location.href;
   // Check specifically for ending with /undefined
   if (currentUrl.endsWith('/undefined')) {
     logDebug('URL ends with /undefined. Checking for toast...');
     const toastContainer = document.querySelector(CONFIG.TOAST_CONTAINER_SELECTOR);
     if (toastContainer) {
       const closeButton = toastContainer.querySelector(CONFIG.TOAST_CLOSE_BUTTON_SELECTOR);
       // Ensure the button is visible before clicking
       if (closeButton && isVisible(closeButton)) {
         logDebug('Found visible toast close button on /undefined page. Clicking...');
         closeButton.click();
         // Optional: Navigate back or to a default page?
         // window.history.back(); // Or window.location.href = '/lab/some/default/page';
       } else {
           logDebug('Toast container found, but close button not found or not visible.');
       }
     } else {
         logDebug('Toast container not found on /undefined page.');
     }
   }
 }

 // --- Event Listeners & Observers ---

 // Keyboard Shortcuts (F7/F8)
 document.addEventListener('keydown', function (event) {
    // Ignore keypresses if a modal is open or if typing in an input/textarea
    if (activeModals.size > 0 || ['INPUT', 'TEXTAREA', 'SELECT'].includes(event.target.tagName)) {
        return;
    }

   if (event.key === 'F7') {
     event.preventDefault(); // Prevent default F7 browser behavior
     logDebug("F7 pressed: Triggering VERIFY1 button click");
     const verify1Button = document.querySelector(CONFIG.VERIFY1_BUTTON_SELECTOR);
     if (verify1Button) verify1Button.click();
     else logDebug("VERIFY1 button not found for F7 shortcut.");
   } else if (event.key === 'F8') {
     event.preventDefault(); // Prevent default F8 browser behavior
     logDebug("F8 pressed: Triggering VERIFY2 button click");
     const verify2Button = document.querySelector(CONFIG.VERIFY2_BUTTON_SELECTOR);
     if (verify2Button) verify2Button.click();
     else logDebug("VERIFY2 button not found for F8 shortcut.");
   }
 });

 // Observer for Success Toasts (for Auto-Back Navigation)
 const toastObserver = new MutationObserver((mutations) => {
   mutations.forEach(mutation => {
     mutation.addedNodes.forEach(node => {
       // Check if the added node is an element and matches the success toast selector
       if (node.nodeType === Node.ELEMENT_NODE && node.matches && node.matches(CONFIG.SUCCESS_TOAST_SELECTOR)) {
         logDebug('Success toast detected. Adding click listener for potential back navigation.');

         // Use a named function for potential removal later if needed
         const handleToastClick = () => {
           logDebug('Success toast clicked.');
           if (verify1Clicked && verify1Toggle) {
             logDebug('Verify1 was active and toggle is ON. Navigating back.');
             window.history.back();
           } else if (verify2Clicked && verify2Toggle) {
             logDebug('Verify2 was active and toggle is ON. Navigating back.');
             window.history.back();
           } else {
               logDebug('Toast clicked, but no active verify toggle matched or toggle was OFF.');
           }
           // Reset flags regardless of navigation
           verify1Clicked = false;
           verify2Clicked = false;
           // Remove listener after click to prevent multiple back navigations if toast persists
           node.removeEventListener('click', handleToastClick);
         };

         node.addEventListener('click', handleToastClick);
       }
     });
   });
 });

 // Main Observer for Page Changes (Adding/Removing Buttons, Starting/Stopping Scan)
 const mainObserver = new MutationObserver((mutations) => {
    // Optimization: Check if relevant nodes were added/removed, e.g., the grid or buttons container
    let potentiallyRelevantChange = false;
    for (const mutation of mutations) {
        if (mutation.type === 'childList') {
            // Basic check: If any nodes were added/removed, re-evaluate state
            if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {
                 potentiallyRelevantChange = true;
                 break;
            }
            // More specific checks could be added here if performance is an issue
        }
    }

    if (!potentiallyRelevantChange) return; // Skip if no relevant DOM changes detected

   // --- Handle Custom Buttons ---
   if (isCorrectPage()) {
       // If on the correct page, ensure buttons are present
       if (!document.getElementById('custom-script-buttons')) {
            addButtons(); // Add buttons if they are missing
       }
   } else {
       // If not on the correct page, ensure buttons are removed
       const buttonDiv = document.getElementById('custom-script-buttons');
       if (buttonDiv) {
           logDebug("Navigated away from edit page, removing custom buttons.");
           buttonDiv.remove();
       }
   }

   // --- Handle Continuous Scanning ---
   // Check if the elements we scan for exist
   const resultsGridExists = document.querySelector(CONFIG.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG.CRITICAL_FLAG_SELECTOR);

   if (resultsGridExists) {
       // If grid exists, ensure scanning is active
       if (!isScanningActive) {
           startContinuousScanning();
       }
   } else {
       // If grid does not exist, ensure scanning is stopped
       if (isScanningActive) {
           stopContinuousScanning();
       }
   }
 });

 // --- Initialization ---
 try {
   logDebug("KAAUH Lab Enhancement Suite Initializing (v5.6.4)...");
   loadFontAwesome(); // Load icons

   // Start checking for the '/undefined' URL issue periodically
   setInterval(checkUrlAndTriggerClickForUndefined, CONFIG.UNDEFINED_URL_CHECK_INTERVAL);
   logDebug(`Started URL check interval (${CONFIG.UNDEFINED_URL_CHECK_INTERVAL}ms) for /undefined toasts.`);

   // Start observing the main body for changes relevant to buttons and scanning
   mainObserver.observe(document.body, { childList: true, subtree: true });
   logDebug("Started main MutationObserver.");

   // Start observing for success toasts
   // Observe the body, assuming toasts are appended there or within a container in the body
   const toastTargetNode = document.body; // Or a more specific container if known e.g., document.getElementById('toast-container-parent')
   toastObserver.observe(toastTargetNode, { childList: true, subtree: true }); // subtree: true if toasts can appear deep
   logDebug("Started toast MutationObserver for back-navigation.");

   // Initial setup checks on window load (covers cases where script loads after initial DOM ready)
   window.addEventListener('load', () => {
     logDebug("Page fully loaded (window.load event). Performing initial checks.");
     // Ensure buttons are added if on the correct page initially
     if (isCorrectPage()) {
         if (!document.getElementById('custom-script-buttons')) {
            addButtons();
         }
     }
     // Start scanning if results grid exists on load
     const resultsGridExists = document.querySelector(CONFIG.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG.CRITICAL_FLAG_SELECTOR);
     if (resultsGridExists && !isScanningActive) {
       startContinuousScanning();
     }
   });

    // Also run initial checks once DOM is ready (might be slightly earlier than window.load)
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            logDebug("DOM fully loaded and parsed (DOMContentLoaded event). Performing initial checks.");
             if (isCorrectPage()) {
                 if (!document.getElementById('custom-script-buttons')) {
                    addButtons();
                 }
             }
             const resultsGridExists = document.querySelector(CONFIG.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG.CRITICAL_FLAG_SELECTOR);
             if (resultsGridExists && !isScanningActive) {
               startContinuousScanning();
             }
        });
    } else {
        // DOM already ready, run checks immediately
        logDebug("DOM already ready. Performing initial checks.");
        if (isCorrectPage()) {
             if (!document.getElementById('custom-script-buttons')) {
                addButtons();
             }
         }
         const resultsGridExists = document.querySelector(CONFIG.RESULT_CELL_SELECTOR) || document.querySelector(CONFIG.CRITICAL_FLAG_SELECTOR);
         if (resultsGridExists && !isScanningActive) {
           startContinuousScanning();
         }
    }


   logDebug("Initialization complete.");

 } catch (error) {
   console.error("[Lab Suite] Critical error during initialization:", error);
   // Attempt to show a modal even if other parts failed
   try {
        showModal("A critical error occurred in the Lab Enhancement Suite. Please check the browser console (F12) for details.");
   } catch (modalError) {
       console.error("[Lab Suite] Could not even display the error modal:", modalError);
       alert("A critical error occurred in the Lab Enhancement Suite script. Check the console (F12)."); // Fallback alert
   }
 }

})(); // End of Userscript IIFE