// ImpactMojo Bookmarks, Compare, and Lo-Fi Features // Direct global attachment (no IIFE to avoid scoping issues) var IMX = window.IMX || {}; window.IMX = IMX; // ======================================== // RICH COURSE DATA FOR COMPARISON // ======================================== var IMPACTMOJO_COURSE_DATA = { 'gender-101': { name: 'Gender 101', type: 'Course', rating: '4.8', learners: '250+', description: 'Comprehensive exploration of gender theory, intersectionality, and social implications in development contexts', objectives: ['Understand foundational gender concepts', 'Analyze intersectionality in practice', 'Apply gender analysis frameworks', 'Develop gender-sensitive designs'], audience: 'Beginners to Intermediate', duration: '4-6 hours', format: 'Self-paced online', url: '/courses/gender/' }, 'meal-101': { name: 'MEAL 101', type: 'Course', rating: '4.9', learners: '350+', description: 'Monitoring, Evaluation, Accountability and Learning frameworks for impact measurement', objectives: ['Design robust M&E frameworks', 'Create effective indicators', 'Implement accountability mechanisms', 'Build learning loops'], audience: 'All levels', duration: '6-8 hours', format: 'Self-paced online', url: '/101-courses/mel-basics.html' }, 'climate-101': { name: 'Climate 101', type: 'Course', rating: '4.7', learners: '180+', description: 'Understanding climate change science, impacts, and resilience strategies for development', objectives: ['Understand climate science', 'Analyze climate impacts', 'Design climate-resilient interventions', 'Integrate climate into programs'], audience: 'Beginners to Intermediate', duration: '5-7 hours', format: 'Self-paced online', url: '/101-courses/climate-essentials.html' }, 'toc-workbench': { name: 'Theory of Change Workbench', type: 'Lab', rating: '4.8', learners: '420+', description: 'Interactive tool for developing and visualizing theory of change', objectives: ['Build comprehensive theories of change', 'Map assumptions and evidence', 'Create visual TOC diagrams', 'Export and share documents'], audience: 'Intermediate to Advanced', duration: '2-4 hours per project', format: 'Interactive web tool', url: '/Labs/toc-lab.html' }, 'logframe-lab': { name: 'LogFrame Lab', type: 'Lab', rating: '4.7', learners: '380+', description: 'Design monitoring, evaluation, and learning frameworks', objectives: ['Create logical frameworks', 'Define SMART indicators', 'Build results chains', 'Export professional documents'], audience: 'Intermediate', duration: '3-5 hours per project', format: 'Interactive web tool', url: '/Labs/mel-plan-lab.html' }, 'storytelling-lab': { name: 'Storytelling Lab', type: 'Lab', rating: '4.8', learners: '290+', description: 'Craft compelling narratives for social impact', objectives: ['Structure impactful stories', 'Use data visualization', 'Create multimedia stories', 'Apply ethical storytelling'], audience: 'All levels', duration: '2-3 hours', format: 'Interactive exercises', url: '/Labs/storytelling-lab.html' }, 'middle-class-game': { name: 'The Middle Class Game', type: 'Game', rating: '4.6', learners: '520+', description: 'Understand who the middle-class really is in India through wealth inequality dynamics', objectives: ['Explore income distribution', 'Understand wealth inequality', 'Challenge economic assumptions', 'Apply economic thinking'], audience: 'All levels', duration: '30-45 minutes', format: 'Interactive simulation', url: '/Games/real-middle-india.html' }, 'prospect-theory': { name: 'Prospect Theory Game', type: 'Game', rating: '4.7', learners: '340+', description: 'Experience behavioral economics through prospect theory and cognitive biases', objectives: ['Understand loss aversion', 'Recognize framing effects', 'Apply behavioral insights', 'Analyze decision-making'], audience: 'Intermediate', duration: '20-30 minutes', format: 'Interactive simulation', url: '/Games/risk-reward-game.html' }, 'public-good-game': { name: 'Public Good Game', type: 'Game', rating: '4.8', learners: '450+', description: 'Experience the free-rider problem and public goods dilemma', objectives: ['Understand public goods economics', 'Experience free-rider dynamics', 'Analyze collective action', 'Explore cooperation mechanisms'], audience: 'All levels', duration: '20-30 minutes', format: 'Interactive simulation', url: '/Games/public-good-game.html' }, 'prisoners-dilemma': { name: 'Prisoners Dilemma', type: 'Game', rating: '4.8', learners: '380+', description: 'Navigate strategic decisions where individual rationality conflicts with collective benefit', objectives: ['Understand the classic dilemma', 'Explore cooperation strategies', 'Analyze repeated games', 'Apply to coordination problems'], audience: 'All levels', duration: '15-25 minutes', format: 'Interactive simulation', url: '/Games/prisoners-dilemma-game.html' } }; // ======================================== // LEARNING ANALYTICS (localStorage-based) // ======================================== IMX.Analytics = { STORAGE_KEY: 'impactmojo_analytics', getData: function() { try { return JSON.parse(localStorage.getItem(this.STORAGE_KEY)) || this.getDefaultData(); } catch(e) { return this.getDefaultData(); } }, getDefaultData: function() { return { totalViews: 0, totalTimeSeconds: 0, sessions: 0, firstVisit: null, lastVisit: null, viewsByItem: {}, recentItems: [], currentSessionStart: null }; }, saveData: function(data) { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data)); }, trackView: function(itemId, itemName, itemType) { var data = this.getData(); data.totalViews++; data.lastVisit = new Date().toISOString(); if (!data.firstVisit) data.firstVisit = data.lastVisit; // Track by item if (!data.viewsByItem[itemId]) { data.viewsByItem[itemId] = { name: itemName, type: itemType, views: 0 }; } data.viewsByItem[itemId].views++; data.viewsByItem[itemId].lastViewed = data.lastVisit; // Track recent items (keep last 10) data.recentItems = data.recentItems.filter(function(i) { return i.id !== itemId; }); data.recentItems.unshift({ id: itemId, name: itemName, type: itemType, time: data.lastVisit }); if (data.recentItems.length > 10) data.recentItems = data.recentItems.slice(0, 10); this.saveData(data); }, startSession: function() { var data = this.getData(); data.sessions++; data.currentSessionStart = Date.now(); this.saveData(data); }, endSession: function() { var data = this.getData(); if (data.currentSessionStart) { var sessionTime = Math.floor((Date.now() - data.currentSessionStart) / 1000); data.totalTimeSeconds += sessionTime; data.currentSessionStart = null; this.saveData(data); } }, getSummary: function() { var data = this.getData(); var topItems = Object.keys(data.viewsByItem) .map(function(key) { return Object.assign({ id: key }, data.viewsByItem[key]); }) .sort(function(a, b) { return b.views - a.views; }) .slice(0, 5); var totalMinutes = Math.floor(data.totalTimeSeconds / 60); var hours = Math.floor(totalMinutes / 60); var mins = totalMinutes % 60; var timeStr = hours > 0 ? hours + 'h ' + mins + 'm' : mins + ' minutes'; return { totalViews: data.totalViews, totalTime: timeStr, totalTimeSeconds: data.totalTimeSeconds, sessions: data.sessions, firstVisit: data.firstVisit ? new Date(data.firstVisit).toLocaleDateString() : 'Never', lastVisit: data.lastVisit ? new Date(data.lastVisit).toLocaleDateString() : 'Never', topItems: topItems, recentItems: data.recentItems.slice(0, 5) }; }, clear: function() { localStorage.removeItem(this.STORAGE_KEY); IMX.showToast('Analytics data cleared'); } }; // Start session on page load IMX.Analytics.startSession(); // End session when leaving window.addEventListener('beforeunload', function() { IMX.Analytics.endSession(); }); // ======================================== // SPEED DIAL MENU // ======================================== IMX.SpeedDial = { isOpen: false, toggle: function() { if (this.isOpen) { this.close(); } else { this.open(); } }, open: function() { var dial = document.getElementById('imxSpeedDial'); if (dial) { dial.classList.add('open'); this.isOpen = true; } }, close: function() { var dial = document.getElementById('imxSpeedDial'); if (dial) { dial.classList.remove('open'); this.isOpen = false; } } }; // Close speed dial on Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && IMX.SpeedDial.isOpen) { IMX.SpeedDial.close(); } }); // ======================================== // DAILY LEARNING TIPS // ======================================== IMX.DailyTip = { tips: [ "Theory of Change is not just a document. It's a living roadmap. Review and update yours quarterly as you learn what works.", "The best M&E systems are designed WITH communities, not FOR them. Participatory approaches increase data quality and ownership.", "Disaggregate your data by gender, age, and location. Averages often hide who's being left behind.", "Qualitative data is not 'soft' data. Stories and context explain the 'why' behind the numbers.", "Baseline data isn't just a requirement. It's your starting point for measuring real change. Don't skip it!", "The Sustainable Development Goals are interconnected. Solving one problem often requires addressing multiple SDGs.", "Cost-effectiveness analysis helps donors compare impact across programs. Learn to calculate and communicate yours.", "Adaptive management means being willing to change course when evidence shows a better path.", "The 'Do No Harm' principle should guide every intervention. Always assess potential negative consequences.", "Capacity building isn't just training. It's about creating systems that sustain after the project ends.", "Mixed methods research combines the best of quantitative and qualitative. Use them together for richer insights.", "Randomized Controlled Trials (RCTs) are the gold standard, but not always feasible. Know your alternatives.", "Social accountability mechanisms empower communities to hold service providers responsible.", "Gender analysis should happen at design stage, not be retrofitted later.", "Systems thinking helps you see the interconnections that simple logic models miss.", "Behavior change takes time. Design your program timelines and expectations accordingly.", "Local ownership is the key to sustainability. Build it from day one.", "Data visualization can tell a story that numbers alone cannot. Learn the basics.", "Ethical considerations in research protect vulnerable populations. Never compromise on informed consent.", "Network effects can amplify your impact. Think about how change spreads through communities." ], currentIndex: 0, init: function() { // Check if element exists before initializing var tipEl = document.getElementById('imxDailyTipText'); if (!tipEl) { console.warn('Daily Tip element not found. Skipping initialization.'); return; } // Show a tip based on the day of year for consistency var dayOfYear = Math.floor((new Date() - new Date(new Date().getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); this.currentIndex = dayOfYear % this.tips.length; this.display(this.tips[this.currentIndex]); }, showRandom: function() { var newIndex; do { newIndex = Math.floor(Math.random() * this.tips.length); } while (newIndex === this.currentIndex && this.tips.length > 1); this.currentIndex = newIndex; this.display(this.tips[this.currentIndex]); // Add a little animation var tipEl = document.getElementById('imxDailyTipText'); if (tipEl) { tipEl.style.opacity = '0'; setTimeout(function() { tipEl.style.opacity = '1'; }, 150); } }, display: function(tip) { var tipEl = document.getElementById('imxDailyTipText'); if (tipEl) { tipEl.textContent = tip; tipEl.style.transition = 'opacity 0.3s ease'; } } }; // Initialize daily tip on page load document.addEventListener('DOMContentLoaded', function() { // Try to initialize immediately if (typeof IMX !== 'undefined' && IMX.DailyTip) { IMX.DailyTip.init(); // If element still shows "Loading tip...", retry after a brief delay setTimeout(function() { var tipEl = document.getElementById('imxDailyTipText'); if (tipEl && tipEl.textContent === 'Loading tip...') { IMX.DailyTip.init(); } }, 500); } }); // ======================================== // SURPRISE ME (Random Content Picker) // ======================================== IMX.surpriseMe = function() { // Collect all content cards var cards = document.querySelectorAll('.card'); if (cards.length === 0) { alert('No content found!'); return; } // Pick a random card var randomIndex = Math.floor(Math.random() * cards.length); var randomCard = cards[randomIndex]; // Scroll to it randomCard.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Add a highlight effect randomCard.style.transition = 'all 0.3s ease'; randomCard.style.boxShadow = '0 0 0 4px #EC4899, 0 0 30px rgba(236, 72, 153, 0.5)'; randomCard.style.transform = 'scale(1.02)'; // Remove highlight after 2 seconds setTimeout(function() { randomCard.style.boxShadow = ''; randomCard.style.transform = ''; }, 2000); // Get the card title for feedback var titleEl = randomCard.querySelector('.card-title, h3'); var title = titleEl ? titleEl.textContent : 'content'; // Show a toast notification (simple) IMX.showToast('Discovered: ' + title); }; // Simple toast notification IMX.showToast = function(message) { // Remove existing toast var existing = document.getElementById('imxToast'); if (existing) existing.remove(); // Create toast var toast = document.createElement('div'); toast.id = 'imxToast'; toast.style.cssText = 'position: fixed; bottom: 100px; left: 50%; transform: translateX(-50%); background: var(--card-bg); color: var(--text-primary); padding: 1rem 1.5rem; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 9999; font-weight: 500; border: 1px solid var(--border-color); animation: toastIn 0.3s ease;'; toast.textContent = message; document.body.appendChild(toast); // Add animation styles if not present if (!document.getElementById('toastStyles')) { var style = document.createElement('style'); style.id = 'toastStyles'; style.textContent = '@keyframes toastIn { from { opacity: 0; transform: translateX(-50%) translateY(20px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }'; document.head.appendChild(style); } // Remove after 3 seconds setTimeout(function() { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s ease'; setTimeout(function() { toast.remove(); }, 300); }, 3000); }; // ======================================== // POMODORO TIMER // ======================================== IMX.Pomodoro = { timer: null, timeLeft: 25 * 60, // seconds totalTime: 25 * 60, isRunning: false, currentLabel: 'Focus Time', STORAGE_KEY: 'imx_pomodoro_stats', init: function() { this.loadStats(); this.updateDisplay(); }, openModal: function() { document.getElementById('imxPomodoroModal').style.display = 'flex'; this.loadStats(); }, closeModal: function() { document.getElementById('imxPomodoroModal').style.display = 'none'; }, start: function() { if (this.isRunning) return; this.isRunning = true; document.getElementById('imxPomodoroStart').style.display = 'none'; document.getElementById('imxPomodoroPause').style.display = 'inline-flex'; document.getElementById('imxPomodoroFab').classList.add('active'); var self = this; this.timer = setInterval(function() { self.timeLeft--; self.updateDisplay(); if (self.timeLeft <= 0) { self.complete(); } }, 1000); }, pause: function() { this.isRunning = false; clearInterval(this.timer); document.getElementById('imxPomodoroStart').style.display = 'inline-flex'; document.getElementById('imxPomodoroPause').style.display = 'none'; document.getElementById('imxPomodoroFab').classList.remove('active'); }, reset: function() { this.pause(); this.timeLeft = this.totalTime; this.updateDisplay(); }, setPreset: function(minutes, label) { this.pause(); this.totalTime = minutes * 60; this.timeLeft = this.totalTime; this.currentLabel = label; this.updateDisplay(); document.getElementById('imxPomodoroLabel').textContent = label; // Update active preset button document.querySelectorAll('.imx-pomodoro-preset').forEach(function(btn) { btn.classList.remove('active'); if (btn.textContent.includes(minutes + ' min')) { btn.classList.add('active'); } }); }, complete: function() { this.pause(); this.playSound(); // Save stats var stats = this.getStats(); stats.sessions++; stats.totalMinutes += Math.floor(this.totalTime / 60); stats.lastDate = new Date().toDateString(); this.saveStats(stats); this.displayStats(stats); // Show notification if (Notification.permission === 'granted') { new Notification('Pomodoro Complete!', { body: this.currentLabel + ' session finished. Great work!', icon: 'https://impactmojo.in/favicon.ico' }); } alert('' + this.currentLabel + ' complete! Take a break.'); this.reset(); }, playSound: function() { try { var audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU'); audio.volume = 0.5; audio.play().catch(function() {}); } catch(e) {} }, updateDisplay: function() { var mins = Math.floor(this.timeLeft / 60); var secs = this.timeLeft % 60; var display = (mins < 10 ? '0' : '') + mins + ':' + (secs < 10 ? '0' : '') + secs; document.getElementById('imxPomodoroTime').textContent = display; }, getStats: function() { try { var stored = localStorage.getItem(this.STORAGE_KEY); if (stored) { var stats = JSON.parse(stored); // Reset if different day if (stats.lastDate !== new Date().toDateString()) { stats.sessions = 0; } return stats; } } catch(e) {} return { sessions: 0, totalMinutes: 0, lastDate: new Date().toDateString() }; }, saveStats: function(stats) { try { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(stats)); } catch(e) {} }, loadStats: function() { var stats = this.getStats(); this.displayStats(stats); }, displayStats: function(stats) { document.getElementById('imxPomodoroSessions').textContent = stats.sessions; var hours = Math.floor(stats.totalMinutes / 60); var mins = stats.totalMinutes % 60; document.getElementById('imxPomodoroTotal').textContent = hours + 'h ' + mins + 'm'; } }; // Request notification permission on load if ('Notification' in window && Notification.permission === 'default') { Notification.requestPermission(); } // ======================================== // SMART READING LISTS (Curated Resources) // ======================================== IMX.ReadingLists = { // SVG icons for reading list categories icons: { chart: '', target: '', balance: '', flask: '', brain: '' }, lists: { 'meal-essentials': { name: 'MEAL Essentials', icon: 'chart', description: 'Core resources for Monitoring, Evaluation, Accountability & Learning', items: [ { title: 'BetterEvaluation Rainbow Framework', url: 'https://www.betterevaluation.org/frameworks-guides/rainbow-framework', type: 'Framework' }, { title: 'USAID Evaluation Toolkit', url: 'https://www.usaid.gov/evaluation', type: 'Toolkit' }, { title: 'World Bank Impact Evaluation Guide', url: 'https://dimewiki.worldbank.org/Impact_Evaluation', type: 'Guide' }, { title: 'ODI Results-Based Management', url: 'https://odi.org/en/publications/results-based-management-in-development-cooperation/', type: 'Publication' }, { title: 'ALNAP Evaluation Resources', url: 'https://www.alnap.org/help-library/evaluation', type: 'Library' } ] }, 'theory-of-change': { name: 'Theory of Change', icon: 'target', description: 'Master the art of designing theories of change', items: [ { title: 'Center for Theory of Change', url: 'https://www.theoryofchange.org/', type: 'Hub' }, { title: 'UNDP Theory of Change Guide', url: 'https://www.undp.org/publications/undp-guidance-note-theory-change', type: 'Guide' }, { title: 'Nesta Theory of Change', url: 'https://www.nesta.org.uk/toolkit/theory-change/', type: 'Toolkit' }, { title: 'Comic Relief TOC Guidance', url: 'https://www.comicrelief.com/our-approach/how-we-measure-our-impact/', type: 'Guide' }, { title: 'DIY Theory of Change Toolkit', url: 'https://diytoolkit.org/tools/theory-of-change/', type: 'Tool' } ] }, 'gender-development': { name: 'Gender in Development', icon: 'balance', description: 'Essential readings on gender analysis and mainstreaming', items: [ { title: 'UN Women Training Centre', url: 'https://trainingcentre.unwomen.org/', type: 'Courses' }, { title: 'BRIDGE Gender & Development', url: 'https://www.bridge.ids.ac.uk/', type: 'Research' }, { title: 'Gender Analysis Toolkit (USAID)', url: 'https://www.usaid.gov/engendering-industries', type: 'Toolkit' }, { title: 'CARE Gender Toolkit', url: 'https://www.care.org/our-work/womens-economic-justice/', type: 'Resources' }, { title: 'World Bank Gender Data Portal', url: 'https://genderdata.worldbank.org/', type: 'Data' } ] }, 'research-methods': { name: 'Research Methods', icon: 'flask', description: 'Qualitative and quantitative research resources', items: [ { title: 'J-PAL Research Resources', url: 'https://www.povertyactionlab.org/research-resources', type: 'Library' }, { title: 'SAGE Research Methods', url: 'https://methods.sagepub.com/', type: 'Database' }, { title: 'Qualitative Research Guidelines (CDC)', url: 'https://www.cdc.gov/healthyyouth/evaluation/pdf/brief19.pdf', type: 'Guide' }, { title: 'IPA Best Practices', url: 'https://www.poverty-action.org/researchers', type: 'Resources' }, { title: '3ie Systematic Reviews', url: 'https://www.3ieimpact.org/evidence-hub/systematic-review-repository', type: 'Repository' } ] }, 'behavioral-economics': { name: 'Behavioral Economics', icon: 'brain', description: 'Apply behavioral insights to development programs', items: [ { title: 'ideas42 Behavioral Design', url: 'https://www.ideas42.org/learn/', type: 'Learning' }, { title: 'Behavioral Insights Team', url: 'https://www.bi.team/publications/', type: 'Publications' }, { title: 'World Bank Mind, Behavior, Development', url: 'https://www.worldbank.org/en/programs/embed', type: 'Program' }, { title: 'Busara Center Resources', url: 'https://busaracenter.org/resources/', type: 'Resources' }, { title: 'OECD Behavioral Insights', url: 'https://www.oecd.org/gov/regulatory-policy/behavioural-insights.htm', type: 'Policy' } ] } }, getList: function(listId) { return this.lists[listId] || null; }, getAllLists: function() { var self = this; return Object.keys(this.lists).map(function(key) { return Object.assign({ id: key }, self.lists[key]); }); } }; // ======================================== // Storage Functions // ======================================== IMX.getBookmarks = function() { try { return JSON.parse(localStorage.getItem('impactmojo_bookmarks') || '[]'); } catch(e) { return []; } }; IMX.saveBookmarks = function(bookmarks) { localStorage.setItem('impactmojo_bookmarks', JSON.stringify(bookmarks)); IMX.updateBadges(); }; IMX.getCompareItems = function() { try { return JSON.parse(localStorage.getItem('impactmojo_compare') || '[]'); } catch(e) { return []; } }; IMX.saveCompareItems = function(items) { localStorage.setItem('impactmojo_compare', JSON.stringify(items)); IMX.updateBadges(); }; // ======================================== // Bookmark Functions // ======================================== IMX.addBookmark = function(item) { var bookmarks = IMX.getBookmarks(); if (!bookmarks.find(function(b) { return b.id === item.id; })) { item.addedAt = new Date().toISOString(); bookmarks.push(item); IMX.saveBookmarks(bookmarks); IMX.showToast('Bookmarked: ' + item.name); } IMX.injectCardButtons(); }; IMX.removeBookmark = function(id) { var bookmarks = IMX.getBookmarks().filter(function(b) { return b.id !== id; }); IMX.saveBookmarks(bookmarks); IMX.renderBookmarksList(); IMX.injectCardButtons(); }; IMX.isBookmarked = function(id) { return IMX.getBookmarks().some(function(b) { return b.id === id; }); }; IMX.clearAllBookmarks = function() { if (confirm('Clear all bookmarks? This cannot be undone.')) { IMX.saveBookmarks([]); IMX.renderBookmarksList(); IMX.injectCardButtons(); IMX.showToast('All bookmarks cleared'); } }; IMX.exportBookmarksCSV = function() { var bookmarks = IMX.getBookmarks(); if (bookmarks.length === 0) { IMX.showToast('No bookmarks to export'); return; } var csv = 'Name,Type,URL,Rating,Learners,Added\n'; bookmarks.forEach(function(b) { csv += '"' + (b.name || '').replace(/"/g, '""') + '",'; csv += '"' + (b.type || '') + '",'; csv += '"' + (b.url || '') + '",'; csv += '"' + (b.rating || '') + '",'; csv += '"' + (b.learners || '') + '",'; csv += '"' + (b.addedAt || '') + '"\n'; }); var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'impactmojo_bookmarks_' + new Date().toISOString().split('T')[0] + '.csv'; link.click(); IMX.showToast('Bookmarks exported!'); }; IMX.renderBookmarksList = function() { var container = document.getElementById('imxBookmarksList'); if (!container) return; var bookmarks = IMX.getBookmarks(); if (bookmarks.length === 0) { container.innerHTML = '
No bookmarks yet
' + 'Click "Save" on any course or lab to add it here' + 'No items to compare
' + 'Click "Compare" on up to 3 courses, labs, or games' + 'No learning activity yet
' + 'Start exploring courses, labs, and games!' + '' + (filterQuery ? 'No notes match your search' : 'No notes yet') + '
' + 'Click "New Note" to get started!
' + '