// ==UserScript==
// @name Enhanced AG Grid Functionality
// @version 4.5.0
// @description Adds vibrant row hover effect, conditional status styling, emergency row highlighting, and 1-min green highlight on row click.
// @match https://his.kaauh.org/lab/*
// @author Hamad AlShegifi (updated by AI)
// @grant GM_addStyle
// @namespace http://tampermonkey.net/
// ==/UserScript==
(function () {
'use strict';
// Constants for row click highlight
const CLICKED_ROW_EXPIRY_PREFIX = 'clicked_row_expiry_';
const CLICK_DURATION_MS = 60 * 1000; // 1 minute
// Inject CSS
GM_addStyle(`
.ag-row {
transition: background-color 0.3s ease;
}
.vibrant-hover {
/* Background color and text color are now applied to cells within the hovered row */
}
.ag-row.vibrant-hover .ag-cell {
background-color: #87dced !important; /* Light blue color */
color: black !important; /* Black text color */
font-weight: bold !important; /* Bold font */
}
.ag-cell[col-id="testStatus"].status-verified-level2,
.ag-cell[col-id="testStatus"].status-verified-level1 {
background-color: #90EE90 !important; /* Light green color */
}
.ag-cell[col-id="testStatus"].status-ordered {
background-color: #FFFFE0 !important; /* Light yellow color */
}
.ag-cell[col-id="testStatus"].status-resulted {
background-color: #FFA500 !important; /* Orange color */
color: black !important; /* Ensure text is readable on orange */
}
.emergency-row {
background-color: #ffe0e0 !important; /* Light red */
}
.ag-cell[col-id="sampleStatus"].status-received {
background-color: #90EE90 !important; /* Light green color for Received sample status */
}
/* Styles for clicked row highlight */
.ag-row.clicked-row-green .ag-cell {
background-color: #A0ECA0 !important; /* A distinct, pleasant green for clicked row */
/* color: black !important; */ /* Optional: if text color needs to change for contrast */
}
`);
// List of col-id attributes to target for hover effect
const targetColumnIdsForHover = [
"orderNo", "testId", "testDescription", "clusterMrn",
"hospitalMrn", "patientName", "dob", "nationalIqamaId",
"department", "clinic", "doctor", "analyzer",
"orderDateAndTime", "lastUpdatedDate", "sampleStatus",
"referenceLab", "accessionNo", "barcode", "sequenceNo",
"primaryPatientId", "referenceLabDesc", "testStatus",
"orderLastModifiedOnEpoch", "orderCreatedOnEpoch", "equipmentName", "doctorName", "localMrn",
"dateOfBirth", "idNumber"
];
const columnsToUncheck = [
'Lab Order No', 'Hospital MRN', 'DOB', 'Test ID', 'National/Iqama Id',
'Department', 'Doctor', 'Analyzer', 'Reference Lab',
'Accession No', 'Sequence No','Age','Container Type','Storage Condition'
];
let hasRunOnce = false; // Prevents running the column unchecking code more than once
// --- START: New functions for persistent green row highlight ---
function handleRowClickForPersistentGreen(event) {
// Ensure the click is on a cell, then find the row
const cellElement = event.target.closest('.ag-cell');
if (!cellElement) return;
const rowElement = cellElement.closest('.ag-row[role="row"]');
if (!rowElement) return;
const barcodeCell = rowElement.querySelector('div[col-id="barcode"]');
if (!barcodeCell || !barcodeCell.textContent) {
// console.log("Clicked row has no barcode cell or content for persistent green.");
return;
}
const barcode = barcodeCell.textContent.trim();
if (!barcode) {
// console.log("Barcode is empty for persistent green.");
return;
}
const expiryTimestamp = Date.now() + CLICK_DURATION_MS;
try {
localStorage.setItem(CLICKED_ROW_EXPIRY_PREFIX + barcode, expiryTimestamp.toString());
// console.log(`[PersistentGreen] Set ${CLICKED_ROW_EXPIRY_PREFIX + barcode} to ${expiryTimestamp}`);
} catch (e) {
console.error("[PersistentGreen] Error saving to localStorage:", e);
}
applyPersistentRowStyles(); // Re-apply styles to all rows to reflect the change
}
function applyPersistentRowStyles() {
const rows = document.querySelectorAll('.ag-center-cols-container div[role="row"], .ag-pinned-left-cols-container div[role="row"], .ag-pinned-right-cols-container div[role="row"]');
const now = Date.now();
// console.log(`[PersistentGreen] Applying styles. Found ${rows.length} rows. Time: ${now}`);
rows.forEach(row => {
const barcodeCell = row.querySelector('div[col-id="barcode"]');
let rowBarcode = null;
if (barcodeCell && barcodeCell.textContent) {
rowBarcode = barcodeCell.textContent.trim();
}
if (rowBarcode) {
const expiryKey = CLICKED_ROW_EXPIRY_PREFIX + rowBarcode;
const expiryTimestampStr = localStorage.getItem(expiryKey);
if (expiryTimestampStr) {
const expiryTimestamp = parseInt(expiryTimestampStr, 10);
if (now < expiryTimestamp) {
row.classList.add('clicked-row-green');
// console.log(`[PersistentGreen] Row ${rowBarcode} is active green. Expires at ${expiryTimestamp}`);
} else {
// console.log(`[PersistentGreen] Row ${rowBarcode} expired. Removing highlight and localStorage item.`);
localStorage.removeItem(expiryKey);
row.classList.remove('clicked-row-green');
}
} else {
// Not in storage, ensure not green
row.classList.remove('clicked-row-green');
}
} else {
// Row has no barcode, ensure it's not green from this mechanism
row.classList.remove('clicked-row-green');
}
});
// General cleanup for expired items in localStorage (for barcodes not currently in DOM)
try {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(CLICKED_ROW_EXPIRY_PREFIX)) {
const expiryTimestampStr = localStorage.getItem(key);
if (expiryTimestampStr) { // Check if item still exists
const expiryTimestamp = parseInt(expiryTimestampStr, 10);
if (now >= expiryTimestamp) {
// console.log(`[PersistentGreen] Cleaning up expired localStorage key: ${key}`);
localStorage.removeItem(key);
}
}
}
}
} catch (e) {
console.error("[PersistentGreen] Error during localStorage cleanup:", e);
}
}
function setupPersistentRowStylesListener() {
const gridRoot = document.querySelector('.ag-root-wrapper'); // Preferred target
let listenerTarget = null;
if (gridRoot) {
listenerTarget = gridRoot;
} else {
const bodyViewport = document.querySelector('.ag-body-viewport');
if (bodyViewport) {
listenerTarget = bodyViewport;
} else {
// console.warn("[PersistentGreen] AG Grid root/viewport not found. Attaching listener to document.body as a fallback.");
listenerTarget = document.body; // Fallback to body
}
}
if (listenerTarget && !listenerTarget.dataset.persistentClickListenerAttached) {
// console.log(`[PersistentGreen] Attaching click listener to ${listenerTarget.tagName || 'document.body'}`);
listenerTarget.addEventListener('click', handleRowClickForPersistentGreen, true); // Using capture phase
listenerTarget.dataset.persistentClickListenerAttached = 'true';
}
}
// --- END: New functions for persistent green row highlight ---
function isSpecificPage() {
return window.location.href.endsWith('/#/lab-orders/lab-test-analyzer');
}
function areColumnsChecked() {
return columnsToUncheck.some(column => isColumnChecked(column));
}
function isColumnChecked(labelText) {
const labels = document.querySelectorAll('.ag-column-tool-panel-column-label');
for (const label of labels) {
if (label.textContent.trim() === labelText) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked');
if (checkbox) return true; // Column is checked
}
}
return false; // Column is not checked
}
function ensureColumnsUnchecked() {
if (hasRunOnce || !isSpecificPage()) return;
if (!areColumnsChecked()) return; // Do nothing if columns are not checked
hasRunOnce = true;
// console.log("Unchecking checked columns...");
setTimeout(() => {
columnsToUncheck.forEach(column => clickColumnLabel(column));
}, 1000);
}
function ensureOtherColumnsChecked() {
if (!isSpecificPage()) return;
// console.log("Ensuring all other columns are checked...");
const allLabels = document.querySelectorAll('.ag-column-tool-panel-column-label');
allLabels.forEach(label => {
const labelText = label.textContent.trim();
if (!columnsToUncheck.includes(labelText)) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-unchecked');
if (checkbox) {
label.click(); // Click to check the column if unchecked
}
}
});
}
function clickColumnLabel(labelText) {
if (!isSpecificPage()) return;
const labels = document.querySelectorAll('.ag-column-tool-panel-column-label');
labels.forEach(label => {
if (label.textContent.trim() === labelText) {
const checkbox = label.parentElement.querySelector('.ag-icon-checkbox-checked');
if (checkbox) {
label.click(); // Click only if checked
}
}
});
}
function highlightEmergencyRows() {
const rows = document.querySelectorAll('div[role="row"]');
rows.forEach(row => {
const clinicCell = row.querySelector('div[col-id="clinic"]');
if (clinicCell && clinicCell.textContent.trim() === 'EMERGENCY') {
row.classList.add('emergency-row');
} else {
row.classList.remove('emergency-row'); // Remove the class if the condition is no longer met
}
});
}
function initColumnToggle() {
if (!isSpecificPage()) return;
// console.log("Checking if columns need to be unchecked and highlighting emergency rows...");
let attempts = 0;
const interval = setInterval(() => {
if (document.querySelector('.ag-side-buttons')) {
ensureColumnsUnchecked();
ensureOtherColumnsChecked();
highlightEmergencyRows(); // Call the highlighting function here
clearInterval(interval);
}
if (++attempts > 10) {
highlightEmergencyRows(); // Ensure highlighting runs even if sidebar isn't found quickly
clearInterval(interval);
}
}, 500);
// Also run highlighting on initial load attempt
highlightEmergencyRows();
}
// Function to apply hover effects to the entire row when any target cell is hovered
function applyHoverEffect() {
document.querySelectorAll('.ag-cell').forEach(cell => {
const colId = cell.getAttribute('col-id');
if (colId && targetColumnIdsForHover.includes(colId)) {
const row = cell.closest('.ag-row'); // Find the parent row
if (row) {
// Add hover effect to the entire row on mouseenter
cell.addEventListener('mouseenter', () => {
row.classList.add('vibrant-hover'); // Add hover effect to the row
});
// Remove hover effect on mouseleave
cell.addEventListener('mouseleave', () => {
row.classList.remove('vibrant-hover'); // Remove hover effect from the row
});
}
}
});
}
// Function to apply conditional background color to the "Status" column
function applyStatusCellStyle() {
document.querySelectorAll('.ag-cell[col-id="testStatus"]').forEach(cell => {
const statusValue = cell.textContent.trim();
cell.classList.remove('status-verified-level2', 'status-verified-level1', 'status-ordered', 'status-resulted'); // Remove previous styles
if (statusValue === "VerifiedLevel2" || statusValue === "VerifiedLevel1") {
cell.classList.add('status-verified-level2');
} else if (statusValue === "Ordered") {
cell.classList.add('status-ordered');
} else if (statusValue === "Resulted") {
cell.classList.add('status-resulted');
}
});
// Apply style for Sample Status = Received
document.querySelectorAll('.ag-cell[col-id="sampleStatus"]').forEach(cell => {
if (cell.textContent.trim() === "Received") {
cell.classList.add('status-received');
} else {
cell.classList.remove('status-received'); // Ensure the class is removed if the value changes
}
});
}
const observer = new MutationObserver(() => {
applyHoverEffect();
applyStatusCellStyle();
highlightEmergencyRows();
applyPersistentRowStyles(); // ADDED
setupPersistentRowStylesListener(); // ADDED: Ensure listener is active/reattached
if (isSpecificPage()) {
hasRunOnce = false;
initColumnToggle();
}
});
// **Important: Observing the correct AG Grid body viewport**
const gridBodyViewport = document.querySelector('.ag-body-viewport'); // Observe the grid body viewport
if (gridBodyViewport) {
observer.observe(gridBodyViewport, { childList: true, subtree: false }); // subtree should be false here as we are observing direct children (rows)
} else {
console.warn("AG Grid body viewport not found for observer. Falling back to body observer.");
observer.observe(document.body, { childList: true, subtree: true });
}
window.addEventListener('load', () => {
applyHoverEffect();
applyStatusCellStyle();
highlightEmergencyRows(); // Ensure consistency with observer
applyPersistentRowStyles(); // ADDED: Apply on initial load
setupPersistentRowStylesListener(); // ADDED: Setup listener on initial load
if (isSpecificPage()) {
initColumnToggle();
}
});
})();
// == The following IIFEs are from the original script and remain unchanged ==
// IIFE for Barcode display box functionality
(function () {
'use strict';
const BARCODE_KEY = 'selectedBarcode';
let currentUrl = location.href;
// Load JsBarcode if needed
function loadJsBarcode(callback) {
if (window.JsBarcode) return callback();
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/JsBarcode.all.min.js';
script.onload = callback;
document.head.appendChild(script);
}
// Insert barcode box
function insertBarcodeBox(barcode) {
if (!barcode) return;
const btnArea = document.querySelector('.btn-area.stickey-btnset');
if (!btnArea || document.getElementById('barcode-svg')) return;
const box = document.createElement('div');
box.style = 'margin-top:10px;padding:8px 12px;background:#f7f7f7;border-radius:8px;display:flex;align-items:center;gap:10px;border:1px solid #ccc;';
const label = document.createElement('div');
label.textContent = 'Sample Barcode:';
label.style = 'font-weight:bold;font-size:14px;color:#333;';
const text = document.createElement('div');
text.textContent = barcode;
text.style = 'font-size:13px;color:#444;';
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.id = "barcode-svg";
svg.style = 'height:40px;width:120px;border:1px solid #ccc;border-radius:4px;padding:2px;';
box.append(label, text, svg);
btnArea.appendChild(box);
loadJsBarcode(() => {
try {
JsBarcode(svg, barcode, {
format: "CODE128",
displayValue: false,
height: 40,
width: 2,
margin: 0
});
// console.log('[✓] Barcode rendered');
} catch (err) {
console.warn('Barcode render error:', err);
}
});
}
// Watch for AG-Grid clicks to collect barcode for the display box
function watchGridClicksForBarcodeBox() {
document.body.addEventListener('click', e => {
const row = e.target.closest('.ag-row');
const cell = row?.querySelector('[col-id="barcode"]');
if (cell) {
const barcode = cell.textContent.trim();
if (barcode) {
localStorage.setItem(BARCODE_KEY, barcode);
// console.log('[✓] Barcode saved for display box:', barcode);
}
}
});
}
// Wait until button area is ready
function waitAndShowBarcode() {
const barcode = localStorage.getItem(BARCODE_KEY);
if (!barcode || !/\/0\/[^/]+\/undefined$/.test(location.href)) return;
const interval = setInterval(() => {
const ready = document.querySelector('.btn-area.stickey-btnset');
if (ready) {
clearInterval(interval);
insertBarcodeBox(barcode);
}
}, 300);
}
// Handle SPA navigation
function observeSPA() {
const checkUrl = () => {
if (location.href !== currentUrl) {
currentUrl = location.href;
// console.log('[URL] Changed (for barcode box):', currentUrl);
waitAndShowBarcode();
}
};
['pushState', 'replaceState'].forEach(type => {
const original = history[type];
history[type] = function () {
const result = original.apply(this, arguments);
setTimeout(checkUrl, 100);
return result;
};
});
window.addEventListener('popstate', checkUrl);
new MutationObserver(checkUrl).observe(document.body, { childList: true, subtree: true });
}
// Init all for barcode box
function initBarcodeBox() {
watchGridClicksForBarcodeBox();
observeSPA();
waitAndShowBarcode();
}
window.addEventListener('load', initBarcodeBox);
})();
// IIFE for Dropdown pagination
(function() {
'use strict';
// Function to set the dropdown value
function setDropdownValue() {
const dropdown = document.getElementById("dropdownPaginationPageSize");
if (dropdown && dropdown.value !== "100") {
dropdown.value = "100"; // Set the value to "100"
// Trigger the 'change' event
const event = new Event('change', { bubbles: true });
dropdown.dispatchEvent(event);
// console.log("Dropdown value set to 100");
}
}
// Function to observe changes in the DOM for the dropdown
function observeDOMForDropdown() {
const observer = new MutationObserver(() => {
setDropdownValue(); // Check and set the dropdown value when changes are detected
});
// Observe the entire document for changes
observer.observe(document.body, {
childList: true,
subtree: true,
});
// console.log("MutationObserver is active for dropdown");
}
// Run the function when the page is fully loaded
window.addEventListener('load', () => {
setDropdownValue(); // Initial check
observeDOMForDropdown(); // Start observing for dynamic changes
});
})();
// IIFE for Ordered Tests Tooltip
(function() {
'use strict';
// Add CSS styles (ensure GM_addStyle is available or manage styles differently if this IIFE is truly isolated)
// Since GM_addStyle is typically called once, this might need adjustment if it's meant to be fully standalone.
// For this integration, assuming GM_addStyle in the main IIFE covers all styles.
// If not, this IIFE would need its own GM_addStyle call or another way to inject CSS.
// For simplicity, I'll assume the main GM_addStyle is sufficient. If tooltip styles are missing,
// they would need to be added to the main GM_addStyle block or injected here.
// The original script had this CSS block here, so I'll keep it, but be mindful of GM_addStyle usage.
GM_addStyle(`
#ordered-tests-tooltip {
position: absolute;
bottom: -10px;
left: 0;
transform: translateY(100%);
background: white;
border: 1px solid #e0e0e0;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: 'Segoe UI', Arial, sans-serif;
display: none;
pointer-events: none;
transition: opacity 0.2s ease;
white-space: nowrap;
max-width: none;
overflow: visible;
min-width: 300px; /* from original */
}
.tooltip-header.resulted-header {
background: #f0fff0;
border-bottom: 1px solid #d0ffd0;
color: #2e7d32;
}
.test-item.completed {
color: #2e7d32;
}
.test-item.completed .test-icon {
color: #4caf50;
font-weight: bold;
margin-right: 8px;
}
.test-item.pending {
color: #d32f2f;
}
.no-tests {
padding: 8px 0;
color: #666;
font-style: italic;
text-align: center;
}
.verify1-btn:hover #ordered-tests-tooltip { /* Corrected selector from original if verify1-btn is the hover target */
display: block;
opacity: 1;
}
.tooltip-header {
background: #fff1f1;
border-bottom: 1px solid #ffd6d6;
padding: 12px 15px;
font-weight: 600;
color: #d32f2f;
display: flex;
align-items: center;
gap: 10px;
white-space: nowrap;
}
.tooltip-content {
padding: 10px 15px;
white-space: nowrap;
}
.test-item {
display: flex;
align-items: center;
gap: 6px;
margin: 2px 0;
font-size: 13px;
}
.test-item:last-child {
border-bottom: none;
}
.test-bullet {
color: #ff5252;
font-size: 14px;
}
.tooltip-footer {
background: #f9f9f9;
border-top: 1px solid #eee;
padding: 8px 15px;
font-size: 11px;
color: #757575;
text-align: right;
white-space: nowrap;
}
.no-ordered-tests { /* This class was in the original CSS block but not used in the HTML structure provided */
padding: 15px;
text-align: center;
color: #666;
font-style: italic;
background: #f8faf8;
white-space: nowrap;
}
`);
const CONFIG = {
POLL_INTERVAL: 1000,
MAX_WAIT_TIME: 30000,
SCAN_INTERVAL: 3000, // This was in original but not obviously used in the provided snippet for this IIFE
COLUMN_PATTERNS: {
TEST_DESC: ['desc', 'testdesc', 'name'],
STATUS: ['status', 'resultstatus', 'state']
}
};
function createTooltip() {
const tooltip = document.createElement('div');
tooltip.id = 'ordered-tests-tooltip';
return tooltip;
}
function findCellByPatterns(row, patterns) {
const cells = row.querySelectorAll('[col-id]');
for (const cell of cells) {
const colId = cell.getAttribute('col-id')?.toLowerCase();
if (patterns.some(pattern => colId?.includes(pattern))) {
return cell;
}
}
return null;
}
// getAllTests was defined in the original script for the tooltip
function getAllTests() {
const leftRows = Array.from(document.querySelectorAll('.ag-pinned-left-cols-container .ag-row'));
const centerRows = Array.from(document.querySelectorAll('.ag-center-cols-container .ag-row'));
const rightRows = Array.from(document.querySelectorAll('.ag-pinned-right-cols-container .ag-row'));
const orderedTests = [];
const resultedTests = [];
// Assuming leftRows is the primary guide for row count
for (let i = 0; i < leftRows.length; i++) {
const leftRow = leftRows[i];
// Gracefully handle cases where center/right rows might not align perfectly (though they should in AG Grid)
const centerRow = centerRows[i] || document.createElement('div'); // Dummy div if not found
const rightRow = rightRows[i] || document.createElement('div'); // Dummy div if not found
const testDescCell = findCellByPatterns(leftRow, CONFIG.COLUMN_PATTERNS.TEST_DESC);
const statusCell = findCellByPatterns(rightRow, CONFIG.COLUMN_PATTERNS.STATUS) ||
findCellByPatterns(centerRow, CONFIG.COLUMN_PATTERNS.STATUS);
const testName = testDescCell?.textContent?.trim();
const status = statusCell?.textContent?.trim().toLowerCase() || '';
if (testName) {
if (status.includes('ordered')) {
orderedTests.push(testName);
} else if (status.includes('resulted')) {
resultedTests.push(testName);
}
}
}
return { orderedTests, resultedTests };
}
function updateTooltipContent(tooltip) {
const now = new Date();
const timeString = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
const dateString = now.toLocaleDateString([], {month: 'short', day: 'numeric'});
const { orderedTests, resultedTests } = getAllTests();
const uniqueOrderedTests = [...new Set(orderedTests)].sort((a, b) =>
a.localeCompare(b, undefined, {sensitivity: 'base'})
);
const uniqueResultedTests = [...new Set(resultedTests)].sort((a, b) =>
a.localeCompare(b, undefined, {sensitivity: 'base'})
);
tooltip.innerHTML = `
<div class="tooltip-header">
<span>⏳ Pending Tests (${uniqueOrderedTests.length})</span>
</div>
<div class="tooltip-content">
${uniqueOrderedTests.length > 0 ?
uniqueOrderedTests.map(test => `
<div class="test-item pending">
<span class="test-bullet">•</span>
<span>${test}</span>
</div>
`).join('') : `
<div class="no-tests">No pending tests</div>`
}
</div>
<div class="tooltip-header resulted-header">
<span>✅ Tests ready for verification (${uniqueResultedTests.length})</span>
</div>
<div class="tooltip-content">
${uniqueResultedTests.length > 0 ?
uniqueResultedTests.map(test => `
<div class="test-item completed">
<span class="test-icon">✓</span>
<span>${test}</span>
</div>
`).join('') : `
<div class="no-tests">No resulted tests</div>`
}
</div>
<div class="tooltip-footer">
Updated: ${dateString} ${timeString}
</div>
`;
}
function attachTooltipToVerifyButton() {
const verifyButton = document.querySelector('.verify1-btn');
if (!verifyButton) return false;
if (verifyButton.querySelector('#ordered-tests-tooltip')) return true; // Already attached
const tooltip = createTooltip();
verifyButton.style.position = 'relative'; // Ensure proper positioning of absolute tooltip
verifyButton.appendChild(tooltip);
verifyButton.addEventListener('mouseenter', () => {
updateTooltipContent(tooltip);
tooltip.style.display = 'block'; // Show on hover
});
verifyButton.addEventListener('mouseleave', () => {
tooltip.style.display = 'none'; // Hide when not hovering
});
return true;
}
function waitForButton() {
const startTime = Date.now();
const checkInterval = setInterval(() => {
if (attachTooltipToVerifyButton() || Date.now() - startTime > CONFIG.MAX_WAIT_TIME) {
clearInterval(checkInterval);
}
}, CONFIG.POLL_INTERVAL);
}
if (document.readyState === 'complete') {
waitForButton();
} else {
window.addEventListener('load', waitForButton);
}
// Watch for SPA navigation to re-attach if button is re-rendered
const tooltipObserver = new MutationObserver(() => {
if (!document.querySelector('.verify1-btn #ordered-tests-tooltip')) {
attachTooltipToVerifyButton();
}
});
tooltipObserver.observe(document.body, { childList: true, subtree: true });
})();