/** * Sets up category toast functionality for mobile */ function setupCategoryToast() { const toastButton = document.getElementById('category-toast-button'); const toastContainer = document.getElementById('category-toast-container'); const toastClose = document.getElementById('category-toast-close'); if (toastButton && toastContainer && toastClose) { // Toggle toast on button click toastButton.addEventListener('click', () => { toastContainer.classList.toggle('active'); }); // Close toast on close button click toastClose.addEventListener('click', () => { toastContainer.classList.remove('active'); }); // Close toast when clicking outside document.addEventListener('click', (e) => { if (!toastContainer.contains(e.target) && e.target !== toastButton) { toastContainer.classList.remove('active'); } }); } }/** * Sets up theme selector functionality */ function setupThemeSelector() { const themeSelect = document.getElementById('theme-select'); // Load saved theme if exists const savedTheme = localStorage.getItem('theme'); if (savedTheme) { document.body.dataset.theme = savedTheme; themeSelect.value = savedTheme; } // Handle theme changes themeSelect.addEventListener('change', (e) => { const selectedTheme = e.target.value; document.body.dataset.theme = selectedTheme; localStorage.setItem('theme', selectedTheme); }); }/** * Sets up mobile menu toggle functionality */ function setupMobileMenu() { const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); const mainNav = document.getElementById('mainnav'); if (mobileMenuToggle && mainNav) { mobileMenuToggle.addEventListener('click', () => { mainNav.classList.toggle('mobile-active'); // Change icon based on menu state const icon = mobileMenuToggle.querySelector('i'); if (mainNav.classList.contains('mobile-active')) { icon.classList.remove('fa-bars'); icon.classList.add('fa-times'); } else { icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } }); // Close mobile menu when a link is clicked mainNav.querySelectorAll('a').forEach(link => { link.addEventListener('click', () => { if (window.innerWidth <= 768) { // Only on mobile screens mainNav.classList.remove('mobile-active'); const icon = mobileMenuToggle.querySelector('i'); icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } }); }); } } document.addEventListener('DOMContentLoaded', () => { // Setup mobile menu setupMobileMenu(); // Setup theme switching setupThemeSelector(); // Navigation handling setupNavigation(); // Check if marked is loaded if (typeof marked === 'undefined') { console.error('marked.js is not loaded!'); document.getElementById('resources-container').innerHTML = `
Error: marked.js library is not loaded properly.
`; return; } // Parse and display content from README.md parseAndDisplayContent() .then(() => { console.log("Content successfully parsed and displayed"); // Show section if page fragment exists if (location.hash) { const hashPart = location.hash.substring(1); document.querySelector(`a[data-category='${hashPart}']`)?.click(); } }) .catch(error => { console.error('Error in main content processing:', error); document.getElementById('resources-container').innerHTML = `
Error loading content: ${error.message}
`; }); }); /** * Sets up navigation between views */ function setupNavigation() { const navHome = document.getElementById('navhome'); const navProfile = document.getElementById('nav-profile'); const navServices = document.getElementById('nav-services'); const navApps = document.getElementById('nav-apps'); navHome.addEventListener('click', (e) => { e.preventDefault(); switchView('home-view'); }); navProfile.addEventListener('click', (e) => { e.preventDefault(); switchView('profile-view'); loadProfileManager(); }); navServices.addEventListener('click', (e) => { e.preventDefault(); switchView('services-view'); loadServices(); }); navApps.addEventListener('click', (e) => { e.preventDefault(); switchView('apps-view'); loadApps(); }); } /** * Switches to the specified view * @param {string} viewId - ID of the view to show */ function switchView(viewId) { // Hide all views document.querySelectorAll('.view').forEach(view => { view.classList.remove('active'); }); // Show the selected view document.getElementById(viewId).classList.add('active'); } /** * Sets up navigation between views */ function setupNavigation() { const navHome = document.getElementById('navhome'); const navProfile = document.getElementById('nav-profile'); const navServices = document.getElementById('nav-services'); const navApps = document.getElementById('nav-apps'); navHome.addEventListener('click', (e) => { e.preventDefault(); switchView('home-view'); }); navProfile.addEventListener('click', (e) => { e.preventDefault(); switchView('profile-view'); loadProfileManager(); }); navServices.addEventListener('click', (e) => { e.preventDefault(); switchView('services-view'); loadServices(); }); navApps.addEventListener('click', (e) => { e.preventDefault(); switchView('apps-view'); loadApps(); }); } /** * Switches to the specified view * @param {string} viewId - ID of the view to show */ function switchView(viewId) { // Hide all views document.querySelectorAll('.view').forEach(view => { view.classList.remove('active'); }); // Show the selected view document.getElementById(viewId).classList.add('active'); } /** * Loads the profile manager functionality */ function loadProfileManager() { const pmContainer = document.getElementById('PM-container'); // This would normally load the profile manager from external scripts // For now, displaying a placeholder message pmContainer.innerHTML = `

Profile Manager_

Profile manager functionality will be loaded here.

This will integrate with the nostr-profile-manager module.

`; } /** * Loads the services view */ function loadServices() { const servicesContainer = document.getElementById('services-container'); // This would normally fetch services from parsed markdown or API // For now, displaying placeholder services servicesContainer.innerHTML = `

NIP-05 Identity Services

Providers that offer NIP-05 identity verification services for Nostr users.

Relay Services

Nostr relay hosting and management services for reliable connectivity.

Storage Services

NIP-96 compatible file storage services for media and document hosting.

`; } /** * Loads the apps view */ function loadApps() { const appsContainer = document.getElementById('apps-container'); // This would normally fetch apps from parsed markdown or API // For now, displaying placeholder apps appsContainer.innerHTML = `

Mobile Clients

Nostr clients for iOS and Android devices.

Web Clients

Browser-based Nostr clients for desktop and mobile access.

Desktop Clients

Native Nostr applications for Windows, macOS, and Linux.

`; } /** * Parse and display content from README.md * @returns {Promise} */ async function parseAndDisplayContent() { try { const response = await fetch('./README.md'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const content = await response.text(); // Parse the markdown into HTML const htmlContent = marked.parse(content); // Create a temporary element to parse the HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = htmlContent; // Get all sections (h2 headers and their content) const sections = {}; let currentSection = null; let currentSectionContent = []; Array.from(tempDiv.children).forEach(element => { if (element.tagName === 'H2') { // If we have a previous section, save it if (currentSection) { sections[currentSection] = currentSectionContent; } // Start new section currentSection = element.textContent.trim(); currentSectionContent = []; } else if (currentSection) { currentSectionContent.push({ type: element.tagName === 'UL' ? 'resources' : 'content', element: element }); } }); // Save the last section if (currentSection) { sections[currentSection] = currentSectionContent; } // Store sections globally window.parsedResources = sections; // Generate navigation const sectionNames = Object.keys(sections); generateNavigation(sectionNames); // Display initial section if (sectionNames.length > 0) { displaySection(sectionNames[0], sections); } // Setup search functionality setupSearch(); } catch (error) { console.error('Error processing markdown:', error); throw error; // Re-throw to be caught by the main error handler } } /** * Generate navigation from content * @param {string[]} sectionNames - Array of section names */ function generateNavigation(sectionNames) { const navList = document.querySelector('#category-list'); const toastList = document.querySelector('#category-toast-list'); // Clear existing navigation navList.innerHTML = ''; toastList.innerHTML = ''; sectionNames.forEach(section => { // Create URL-friendly ID const sectionId = section .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, ''); // Add to regular list (desktop) const li = document.createElement('li'); li.innerHTML = ` ${section} `; // Add click handler li.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); // Remove active class from all links in both desktop and toast document.querySelectorAll('#category-list li, #category-toast-list li').forEach(item => item.classList.remove('active') ); // Add active class to clicked link and its toast counterpart li.classList.add('active'); document.querySelectorAll(`#category-toast-list li a[data-category="${sectionId}"]`) .forEach(link => link.parentElement.classList.add('active')); // Display the section displaySection(section, window.parsedResources); // Close the toast on mobile document.getElementById('category-toast-container').classList.remove('active'); }); navList.appendChild(li); // Create duplicate for toast menu (mobile) const toastLi = li.cloneNode(true); // Add click handler for toast items toastLi.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); // Remove active class from all links in both desktop and toast document.querySelectorAll('#category-list li, #category-toast-list li').forEach(item => item.classList.remove('active') ); // Add active class to clicked link and its desktop counterpart toastLi.classList.add('active'); document.querySelectorAll(`#category-list li a[data-category="${sectionId}"]`) .forEach(link => link.parentElement.classList.add('active')); // Display the section displaySection(section, window.parsedResources); // Close the toast document.getElementById('category-toast-container').classList.remove('active'); }); toastList.appendChild(toastLi); }); // Set first item as active in both menus const firstDesktopLink = navList.querySelector('li'); const firstToastLink = toastList.querySelector('li'); if (firstDesktopLink) { firstDesktopLink.classList.add('active'); } if (firstToastLink) { firstToastLink.classList.add('active'); } // Setup category toast button setupCategoryToast(); } /** * Display a specific section of content * @param {string} sectionName - Name of the section to display * @param {Object} sections - Object containing all sections */ function displaySection(sectionName, sections) { const container = document.getElementById('resources-container'); container.innerHTML = ''; // Add section header const header = document.createElement('h3'); header.textContent = sectionName; container.appendChild(header); // Display section content sections[sectionName].forEach(item => { if (item.type === 'content') { // Regular markdown content const contentDiv = document.createElement('div'); contentDiv.className = 'markdown-content'; contentDiv.innerHTML = item.element.outerHTML; container.appendChild(contentDiv); } else if (item.type === 'resources') { // Resource list Array.from(item.element.children).forEach(li => { const card = createResourceCard({ name: li.querySelector('a')?.textContent || '', link: li.querySelector('a')?.href || '', description: li.textContent.split('- ')[1]?.trim() || '', stars: li.querySelector('img[alt="stars"]') ? parseInt(li.querySelector('img[alt="stars"]').src.match(/stars\/(\d+)/)?.[1]) || 0 : 0 }); container.appendChild(card); }); } }); } /** * Creates a resource card element from data * @param {Object} resource - Resource data * @returns {HTMLElement} - Resource card element */ function createResourceCard(resource) { const card = document.createElement('div'); card.className = 'resource-card'; // Stars display if available const starsDisplay = resource.stars ? ` ${resource.stars}` : ''; card.innerHTML = `

${resource.name} ${starsDisplay}

${resource.description}

`; return card; } /** * Sets up search functionality */ function setupSearch() { const searchInput = document.getElementById('search-input'); const searchButton = document.getElementById('search-button'); // Search on button click searchButton.addEventListener('click', () => { performSearch(searchInput.value); }); // Search on Enter key searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { performSearch(searchInput.value); } }); } /** * Performs search across all resources * @param {string} searchTerm - Term to search for */ function performSearch(searchTerm) { const container = document.getElementById('resources-container'); if (!searchTerm) { // If search is empty, restore current category view const currentCategory = document.querySelector('#category-list li.active a')?.textContent; if (currentCategory) { displaySection(currentCategory, window.parsedResources); } return; } // Clear current container container.innerHTML = ''; // Add search results header const header = document.createElement('h3'); header.textContent = `Search Results for "${searchTerm}"`; container.appendChild(header); // Search through all sections let resultsFound = false; Object.entries(window.parsedResources).forEach(([sectionName, sectionContent]) => { sectionContent.forEach(item => { if (item.type === 'resources') { // Process resource items for search searchResourceList(item.element, container, searchTerm, sectionName); resultsFound = true; } else if (item.type === 'content') { // Search through regular content const contentText = item.element.textContent.toLowerCase(); if (contentText.includes(searchTerm.toLowerCase())) { const contentDiv = document.createElement('div'); contentDiv.className = 'markdown-content'; contentDiv.innerHTML = item.element.outerHTML; // Add section label const sectionLabel = document.createElement('div'); sectionLabel.className = 'category-label'; sectionLabel.textContent = sectionName; container.appendChild(sectionLabel); container.appendChild(contentDiv); resultsFound = true; } } }); }); // Show "no results" message if nothing found if (!resultsFound) { container.innerHTML += `

No resources found matching "${searchTerm}"

`; } } /** * Search through a resource list * @param {HTMLElement} ulElement - UL element containing resources * @param {HTMLElement} container - Container to add results to * @param {string} searchTerm - Term to search for * @param {string} sectionName - Name of the section being searched */ function searchResourceList(ulElement, container, searchTerm, sectionName) { Array.from(ulElement.children).forEach(li => { // Search in the main item if it has a link if (li.querySelector(':scope > a')) { const resourceName = li.querySelector(':scope > a')?.textContent || ''; const resourceLink = li.querySelector(':scope > a')?.href || ''; const description = li.textContent .replace(resourceName, '') // Remove the resource name .replace(/^\s*-\s*/, '') // Remove leading dash .replace(/\s*\[!\[.*?\]\(.*?\)\]\(.*?\)\s*/, '') // Remove GitHub stars badge if present .trim(); const searchableText = [resourceName, description, resourceLink] .join(' ') .toLowerCase(); if (searchableText.includes(searchTerm.toLowerCase())) { const card = createResourceCard({ name: resourceName, link: resourceLink, description: description, stars: li.querySelector(':scope > img[alt="stars"]') ? parseInt(li.querySelector(':scope > img[alt="stars"]').src.match(/stars\/(\d+)/)?.[1]) || 0 : 0 }); // Add section label to card const sectionLabel = document.createElement('div'); sectionLabel.className = 'category-label'; sectionLabel.textContent = sectionName; card.insertBefore(sectionLabel, card.firstChild); container.appendChild(card); } } // Search in nested items if they exist const nestedUl = li.querySelector(':scope > ul'); if (nestedUl) { searchResourceList(nestedUl, container, searchTerm, sectionName); } }); }