/** * 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 === 'INSPECT_LIVE_FEED') { (async () => { try { const studentId = message.studentId || detectCurrentStudentId(); const apiListRes = await fetch(`https://schools.mybrightwheel.com/api/v1/students/${studentId}/activities?page_size=25`, { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json()).catch(e => ({ error: e.message })); const rawActs = apiListRes?.activities || apiListRes?.items || []; // Sample detail fetches for video, nap, checkin const sampleDetails = {}; for (const act of rawActs.slice(0, 10)) { const type = act.activity_type || act.action_type || act.type; if (!sampleDetails[type] && act.id) { const detail = await fetch(`https://schools.mybrightwheel.com/api/v1/students/${studentId}/activities/${act.id}`, { credentials: 'include', headers: { 'Accept': 'application/json' } }).then(r => r.json()).catch(e => ({ error: e.message })); sampleDetails[type] = detail; } } const domCards = scrapeDomFeedCards(); sendResponse({ success: true, studentId, apiSampleActivities: rawActs.slice(0, 10), sampleDetails, domCards: domCards.slice(0, 20), pageTitle: document.title, currentUrl: window.location.href }); } catch (err) { sendResponse({ success: false, error: err.message }); } })(); return true; // async } // DOM Scraper for Brightwheel Web Feed function scrapeDomFeedCards() { const results = []; // Broad selector covering common Brightwheel feed cards const cardSelectors = [ '[data-testid*="activity"]', '[data-testid*="feed"]', '.activity-item', '.feed-item', '.timeline-item', 'article', '[class*="ActivityCard"]', '[class*="feed-card"]', '[class*="FeedItem"]', '[class*="TimelineItem"]' ]; let cards = document.querySelectorAll(cardSelectors.join(',')); if (!cards || cards.length === 0) { // Fallback: look for cards by common structure cards = document.querySelectorAll('div[class*="card"], div[class*="Card"]'); } cards.forEach((card, index) => { const text = card.innerText || ''; if (!text.trim()) return; const cardId = card.getAttribute('data-id') || card.getAttribute('data-activity-id') || card.id || `dom_${index}`; const img = card.querySelector('img[src*="cdn"], img[src*="brightwheel"], img[src*="cloudfront"], img[src*="s3"]'); const video = card.querySelector('video, source, a[href*=".mp4"], a[href*="video"]'); const videoUrl = video?.src || video?.getAttribute('src') || video?.getAttribute('href') || null; const imageUrl = img?.src || null; results.push({ dom_id: cardId, raw_text: text, image_url: imageUrl, video_url: videoUrl, class_name: card.className, html_snippet: card.outerHTML.substring(0, 800) }); }); return results; } // 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(); } })();