/** * Brightwheel Exporter - Content Script * Injected on https://schools.mybrightwheel.com/* */ (function () { console.log('[Brightwheel Exporter] Content script initialized.'); // Extract student ID from URL if present function detectCurrentStudentId() { const match = window.location.pathname.match(/\/students\/([a-zA-Z0-9-]+)/); if (match && match[1]) { chrome.storage.local.set({ lastActiveStudentId: match[1] }); return match[1]; } return null; } detectCurrentStudentId(); // Watch for SPA URL changes let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; detectCurrentStudentId(); } }).observe(document, { subtree: true, childList: true }); // Listen for messages from extension popup or dashboard chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.action === 'GET_PAGE_CONTEXT') { const studentId = detectCurrentStudentId(); sendResponse({ url: window.location.href, studentId: studentId, cookiesPresent: document.cookie.includes('_brightwheel_v2') }); return true; } if (message.action === 'FETCH_WITH_CREDENTIALS') { // Execute fetch inside page context with credentials fetch(message.url, { method: message.method || 'GET', headers: { 'Accept': 'application/json', ...(message.headers || {}) }, credentials: 'include' }) .then(async (res) => { if (!res.ok) { const errorText = await res.text().catch(() => ''); return sendResponse({ success: false, status: res.status, statusText: res.statusText, body: errorText }); } const json = await res.json(); sendResponse({ success: true, status: res.status, data: json }); }) .catch((err) => { sendResponse({ success: false, error: err.message }); }); return true; // async } }); // Inject a discreet floating button on Brightwheel page function injectExporterButton() { if (document.getElementById('bw-exporter-floating-btn')) return; const btn = document.createElement('button'); btn.id = 'bw-exporter-floating-btn'; btn.innerHTML = ` Export Brightwheel Data `; Object.assign(btn.style, { position: 'fixed', bottom: '24px', right: '24px', zIndex: '999999', display: 'inline-flex', alignItems: 'center', padding: '10px 18px', background: 'linear-gradient(135deg, #FF6B35 0%, #FF8E53 100%)', color: '#FFFFFF', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: '14px', fontWeight: '600', border: 'none', borderRadius: '30px', boxShadow: '0 4px 16px rgba(255, 107, 53, 0.4), 0 2px 6px rgba(0, 0, 0, 0.1)', cursor: 'pointer', transition: 'all 0.25s ease', }); btn.addEventListener('mouseenter', () => { btn.style.transform = 'translateY(-2px) scale(1.03)'; btn.style.boxShadow = '0 6px 20px rgba(255, 107, 53, 0.5), 0 3px 8px rgba(0, 0, 0, 0.15)'; }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'translateY(0) scale(1)'; btn.style.boxShadow = '0 4px 16px rgba(255, 107, 53, 0.4), 0 2px 6px rgba(0, 0, 0, 0.1)'; }); btn.addEventListener('click', () => { chrome.runtime.sendMessage({ action: 'OPEN_DASHBOARD' }); }); document.body.appendChild(btn); } // Inject when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', injectExporterButton); } else { injectExporterButton(); } })();