Directory: . ================================================== File: ./web/style.css.new -------------------------------------------------- /* DNA Helix Animation */ .dna-helix { position: relative; width: 200px; height: 400px; margin: 0 auto; perspective: 1200px; transform-style: preserve-3d; } .base-pair { position: absolute; width: 100%; height: 20px; animation: rotate 8s linear infinite; transform-style: preserve-3d; } .base { position: absolute; width: 25px; height: 25px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; text-shadow: 0 0 3px rgba(0,0,0,0.5); box-shadow: 0 0 15px rgba(0,0,0,0.2); } .left-base { left: 35px; background: #FF0066; animation: pulse 3s ease-in-out infinite; filter: brightness(1.1); } .right-base { right: 35px; background: #00B7FF; animation: pulse 3s ease-in-out infinite; filter: brightness(1.1); } .base-connector { position: absolute; left: 50%; width: 90px; height: 3px; background: linear-gradient(90deg, rgba(255, 0, 102, 0.8), rgba(255, 255, 255, 0.3) 50%, rgba(0, 183, 255, 0.8) ); transform: translateX(-50%); box-shadow: 0 0 8px rgba(0,0,0,0.15); } @keyframes rotate { 0% { transform: rotateY(0deg) translateZ(20px); } 100% { transform: rotateY(360deg) translateZ(20px); } } @keyframes pulse { 0% { transform: scale(1); opacity: 0.9; filter: brightness(1.1); } 50% { transform: scale(1.15); opacity: 1; filter: brightness(1.3); } 100% { transform: scale(1); opacity: 0.9; filter: brightness(1.1); } } -------------------------------------------------- File: ./web/humans.txt -------------------------------------------------- contact@dnanalyzer.orgDNAnalyzer is built by a large team of engineers, designers, researchers, robots, and others in many different sites across the globe. It is updated continuously, and built with more tools and technologies than we can shake a stick at. If you'd like to help us out, see www.github.com/VerisimilitudeX/DNAnalyzer. We're always looking for more talent, and we're always looking for more ideas. If you have any suggestions, please let us know! We're always looking for ways to improve the site. If you have any questions, please feel free to contact us at contact@dnanalyzer.live. We'd love to hear from you! -------------------------------------------------- File: ./web/CITATION.cff -------------------------------------------------- cff-version: 1.2.0 authors: - family-names: Acharya given-names: Piyush orcid: "https://orcid.org/0009-0000-4726-5928" date-released: 2022-10-10 message: "If you use this software, please cite it as below." title: "DNAnalyzer: ML-Powered DNA Analysis Platform" version: "3.5.0-beta.0" url: "https://github.com/VerisimilitudeX/DNAnalyzer" doi: "10.5281/zenodo.14556577" -------------------------------------------------- File: ./web/all_files.py -------------------------------------------------- import os def save_directory_structure_and_files(root_dir, output_file): with open(output_file, 'w', encoding='utf-8') as out: for dirpath, dirnames, filenames in os.walk(root_dir): # Write the directory name rel_path = os.path.relpath(dirpath, root_dir) out.write(f"Directory: {rel_path}\n") out.write("=" * 50 + "\n") # Write file names and contents for file in filenames: file_path = os.path.join(dirpath, file) out.write(f"File: {file_path}\n") # Include full file path out.write("-" * 50 + "\n") try: with open(file_path, 'r', encoding='utf-8') as f: out.write(f.read()) except Exception as e: out.write(f"[Could not read file: {e}]\n") out.write("\n" + "-" * 50 + "\n") out.write("\n") if __name__ == "__main__": root_directory = "./web" output_text_file = "web_structure.txt" save_directory_structure_and_files(root_directory, output_text_file) print(f"Directory structure and files saved to {output_text_file}") -------------------------------------------------- File: ./web/index.js -------------------------------------------------- /** * DNAnalyzer Main JavaScript * Handles animations, interactivity and UI functionality */ document.addEventListener('DOMContentLoaded', function() { initNotificationBanner(); initNotificationScroll(); // Must run after banner init to get height initDNAHelix(); initMobileMenu(); initNavbarScroll(); initStatsAnimation(); initSmoothScroll(); initScrollAnimations(); // Initialize general scroll animations // Log initialization console.log("DNAnalyzer UI Initialized"); }); // Shared state for notification banner let notificationClosed = false; let notificationHeight = 0; // Store banner height globally /** * Initialize the notification banner dismiss functionality and store its height */ function initNotificationBanner() { const banner = document.querySelector('.notification-banner'); if (!banner) return; notificationHeight = banner.offsetHeight; document.documentElement.style.setProperty('--notification-height', `${notificationHeight}px`); console.log(`Notification banner height: ${notificationHeight}px`); const closeBtn = banner.querySelector('.notification-close'); if (!closeBtn) return; closeBtn.addEventListener('click', function() { banner.classList.add('closed'); // Hide immediately for visual feedback notificationHeight = 0; // Set height to 0 document.documentElement.style.setProperty('--notification-height', '0px'); notificationClosed = true; adjustNavbarPosition(); // Adjust navbar immediately console.log("Notification banner closed"); }); } /** * Adjusts the navbar's top position based on the notification banner's visibility. */ function adjustNavbarPosition() { const navbar = document.getElementById('navbar'); if (!navbar) return; const currentScroll = window.pageYOffset || document.documentElement.scrollTop; const banner = document.querySelector('.notification-banner'); // If banner is closed or hidden by scroll, navbar top is 0 if (notificationClosed || (banner && banner.classList.contains('hide-notification')) || currentScroll > notificationHeight) { navbar.style.top = '0px'; } // Otherwise, position navbar below the banner else if (!notificationClosed && banner && !banner.classList.contains('hide-notification')) { navbar.style.top = `${notificationHeight}px`; } // Default case if something goes wrong else { navbar.style.top = '0px'; } } /** * Initialize notification banner scroll behavior (hide on scroll down, show on scroll up) */ function initNotificationScroll() { const banner = document.querySelector('.notification-banner'); if (!banner) return; let lastScrollTop = 0; const scrollThreshold = 50; // Pixels to scroll before hiding/showing window.addEventListener('scroll', function() { let scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Ignore minor scroll fluctuations if (Math.abs(scrollTop - lastScrollTop) <= 5) { return; } if (scrollTop > lastScrollTop && scrollTop > scrollThreshold) { // Scrolling Down if (!banner.classList.contains('hide-notification') && !notificationClosed) { banner.classList.add('hide-notification'); adjustNavbarPosition(); // Adjust navbar when banner hides console.log("Hiding notification on scroll down"); } } else if (scrollTop < lastScrollTop || scrollTop <= scrollThreshold) { // Scrolling Up or near top if (banner.classList.contains('hide-notification') && !notificationClosed) { banner.classList.remove('hide-notification'); adjustNavbarPosition(); // Adjust navbar when banner shows console.log("Showing notification on scroll up"); } } // Ensure correct navbar position even if banner wasn't hidden/shown if (scrollTop <= scrollThreshold && !notificationClosed) { banner.classList.remove('hide-notification'); adjustNavbarPosition(); } else if (scrollTop > scrollThreshold && !notificationClosed && banner.classList.contains('hide-notification')) { adjustNavbarPosition(); // Keep navbar at top if banner is hidden } else if (notificationClosed) { adjustNavbarPosition(); // Keep navbar at top if banner is closed } lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; // For Mobile or negative scrolling }, { passive: true }); // Improve scroll performance } /** * Initialize the DNA Helix animation on the homepage */ function initDNAHelix() { const dnaHelixContainer = document.getElementById('dnaHelix'); if (!dnaHelixContainer) return; dnaHelixContainer.innerHTML = ''; // Clear existing const basePairsConfig = [ { pair: ['A', 'T'], leftColor: 'var(--magenta)', rightColor: 'var(--blue)' }, { pair: ['T', 'A'], leftColor: 'var(--blue)', rightColor: 'var(--magenta)' }, { pair: ['G', 'C'], leftColor: 'var(--orange)', rightColor: '#34c759' }, // Added green { pair: ['C', 'G'], leftColor: '#34c759', rightColor: 'var(--orange)' } ]; const numBasePairs = 25; // Total number of pairs const verticalSpacing = 20; // Space between pairs const helixRadius = 50; // How far out the bases extend (Z-axis) const rotationIncrement = 20; // Degrees twist per base pair const animationDuration = 10; // seconds for full rotation for (let i = 0; i < numBasePairs; i++) { const config = basePairsConfig[i % basePairsConfig.length]; // Cycle through colors/pairs const [leftBaseChar, rightBaseChar] = config.pair; const basePairElement = document.createElement('div'); basePairElement.className = 'base-pair'; // Vertical position const yPos = (i - numBasePairs / 2) * verticalSpacing; // Center the helix vertically basePairElement.style.transform = `translateY(${yPos}px)`; // Initial Y position // Calculate initial rotation angle and offsets for the helix shape const angle = (i * rotationIncrement) % 360; const xOffset = Math.sin(angle * Math.PI / 180) * 15; // Sideways curve // Store initial transform values in CSS variables for the animation basePairElement.style.setProperty('--start-angle', `${angle}deg`); basePairElement.style.setProperty('--x-offset', `${xOffset}px`); // Apply animation const delay = (i * 0.1) % animationDuration; // Staggered animation start basePairElement.style.animation = `rotate ${animationDuration}s linear infinite ${-delay}s`; // Create Left Base const leftBase = document.createElement('div'); leftBase.className = 'base left-base'; leftBase.textContent = leftBaseChar; leftBase.style.background = config.leftColor; leftBase.style.animationDelay = `${(i * 0.15)}s`; // Stagger pulse // Create Right Base const rightBase = document.createElement('div'); rightBase.className = 'base right-base'; rightBase.textContent = rightBaseChar; rightBase.style.background = config.rightColor; rightBase.style.animationDelay = `${(i * 0.15 + 0.1)}s`; // Stagger pulse slightly more // Create Connector const connector = document.createElement('div'); connector.className = 'base-connector'; // Optional: Style connector dynamically if needed // Append elements basePairElement.appendChild(leftBase); basePairElement.appendChild(rightBase); basePairElement.appendChild(connector); // Connector should be behind bases visually if needed dnaHelixContainer.appendChild(basePairElement); } // Adjust container height if needed (optional) // dnaHelixContainer.style.height = `${numBasePairs * verticalSpacing + 100}px`; } /** * Initialize the mobile menu toggle functionality */ function initMobileMenu() { const mobileToggle = document.getElementById('mobileToggle'); const navLinks = document.getElementById('navLinks'); if (!mobileToggle || !navLinks) return; mobileToggle.addEventListener('click', function() { const isActive = navLinks.classList.toggle('active'); mobileToggle.setAttribute('aria-expanded', isActive); const icon = mobileToggle.querySelector('i'); if (isActive) { icon.classList.remove('fa-bars'); icon.classList.add('fa-times'); } else { icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } }); // Close menu when a link is clicked navLinks.querySelectorAll('a').forEach(link => { link.addEventListener('click', () => { if (navLinks.classList.contains('active')) { navLinks.classList.remove('active'); mobileToggle.setAttribute('aria-expanded', 'false'); const icon = mobileToggle.querySelector('i'); icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } }); }); } /** * Initialize the navbar scroll effect (adding 'scrolled' class) */ function initNavbarScroll() { const navbar = document.getElementById('navbar'); if (!navbar) return; const scrollThreshold = 30; // Pixels scrolled before adding class window.addEventListener('scroll', function() { if (window.pageYOffset > scrollThreshold) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }, { passive: true }); } /** * Animate number counting from 0 to target with easing */ function animateNumber(element, targetString, suffix = '', duration = 2000) { if (!element) return; const target = parseFloat(targetString.replace(/[^0-9.]/g, '')); // Extract number if (isNaN(target)) { element.textContent = targetString + suffix; // Fallback for non-numeric targets return; } let start = 0; let startTime = null; // Easing function (easeOutExpo) function easeOutExpo(t) { return t === 1 ? 1 : 1 - Math.pow(2, -10 * t); } function animationStep(timestamp) { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / duration, 1); const easedProgress = easeOutExpo(progress); let currentValue = Math.floor(easedProgress * target); // Handle M+ and + suffixes correctly let displaySuffix = suffix; if (targetString.includes('M+')) { currentValue = (easedProgress * target).toFixed(1); // Show decimal during animation displaySuffix = 'M+'; if (progress === 1) currentValue = target; // Ensure final value is integer if needed } else if (targetString.includes('+') && suffix === '') { // Check if suffix wasn't passed but '+' exists displaySuffix = '+'; } // Prevent displaying 0M+ or 0+ initially if (elapsed < 50 && currentValue === 0 && displaySuffix) { element.textContent = 0; } else { element.textContent = `${currentValue}${displaySuffix}`; } if (progress < 1) { requestAnimationFrame(animationStep); } else { // Ensure final display is exactly the target string element.textContent = targetString.replace(/([0-9.]+)/, target) + displaySuffix; } } requestAnimationFrame(animationStep); } /** * Initialize the stats counter animation with Intersection Observer */ function initStatsAnimation() { const statsSection = document.querySelector('.stats-section'); if (!statsSection) return; const statElements = [ // Use array for easier iteration { elem: document.getElementById('statAccuracy'), target: '141', suffix: '' }, { elem: document.getElementById('statSequences'), target: '7M+', suffix: '' }, // Suffix handled in animateNumber { elem: document.getElementById('statUsers'), target: '46+', suffix: '' } // Suffix handled in animateNumber ]; let animated = false; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !animated) { console.log("Stats section intersecting, animating numbers."); statElements.forEach((stat, index) => { setTimeout(() => { if (stat.elem) { // Pass target string directly, animateNumber handles extraction animateNumber(stat.elem, stat.target, stat.suffix, 2000 + index * 100); } }, index * 200); // Stagger animation start }); animated = true; observer.unobserve(statsSection); // Stop observing once animated } }); }, { threshold: 0.3 }); // Trigger when 30% visible observer.observe(statsSection); } /** * Add smooth scrolling for anchor links within the page */ function initSmoothScroll() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { const targetId = this.getAttribute('href'); // Ensure it's a valid internal link and not just "#" if (targetId && targetId.length > 1 && targetId.startsWith('#')) { try { const targetElement = document.querySelector(targetId); if (targetElement) { e.preventDefault(); const navbarHeight = document.getElementById('navbar')?.offsetHeight || 70; // Get current navbar height or default const elementPosition = targetElement.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - navbarHeight - 20; // Extra 20px padding window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } } catch (error) { console.warn(`Smooth scroll target not found or invalid selector: ${targetId}`, error); } } }); }); } /** * Initialize Intersection Observer for general scroll-triggered animations */ function initScrollAnimations() { const animatedElements = document.querySelectorAll('.scroll-animate, .scroll-animate-left, .scroll-animate-right, .scroll-animate-scale'); if (!animatedElements.length) return; const observerOptions = { threshold: 0.15, // Trigger when 15% of the element is visible rootMargin: '0px 0px -50px 0px' // Trigger slightly before element fully enters viewport bottom }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-in'); observer.unobserve(entry.target); // Stop observing once animated } }); }, observerOptions); animatedElements.forEach(el => { observer.observe(el); }); console.log(`Observing ${animatedElements.length} elements for scroll animations.`); } -------------------------------------------------- File: ./web/style.css -------------------------------------------------- /* DNAnalyzer - Main Stylesheet * Global styles for all pages */ /* Notification Banner Styling */ .notification-banner { background: linear-gradient(135deg, var(--orange), #FFB347); /* Adjusted gradient slightly */ color: var(--dark-bg); /* Dark text for contrast */ padding: 0.75rem 2.5rem 0.75rem 1rem; /* More padding on right for close button */ text-align: center; font-size: 0.95rem; /* Slightly smaller */ font-weight: 500; /* Normal weight */ position: fixed; top: 0; left: 0; width: 100%; z-index: 1000; /* High z-index */ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); transition: transform 0.4s ease-in-out, opacity 0.4s ease-in-out; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } .notification-banner.hide-notification { transform: translateY(-100%); opacity: 0; pointer-events: none; } .notification-banner.closed { display: none !important; /* Use !important carefully, needed here to override */ } .notification-banner .notification-link { color: var(--dark-bg); /* Dark text */ text-decoration: underline; font-weight: 600; /* Bold link */ background-color: rgba(255, 255, 255, 0.3); padding: 0.2rem 0.4rem; border-radius: 4px; margin: 0 0.4rem; transition: all 0.2s ease; white-space: nowrap; } .notification-banner .notification-link:hover { background-color: rgba(255, 255, 255, 0.5); color: #000; /* Black on hover */ text-decoration: none; } .notification-banner .notification-close { background: none; /* Transparent background */ border: none; color: var(--dark-bg); /* Dark color */ font-size: 1.5rem; /* Larger close icon */ line-height: 1; position: absolute; right: 0.75rem; /* Positioned closer */ top: 50%; transform: translateY(-50%); cursor: pointer; transition: all 0.2s ease; width: 30px; /* Larger hit area */ height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; padding: 0; opacity: 0.7; } .notification-banner .notification-close:hover { background: rgba(0, 0, 0, 0.1); opacity: 1; transform: translateY(-50%) rotate(90deg); } /* --- Root Variables --- */ :root { /* Brand colors from logo */ --magenta: #ff0066; --blue: #00a4ef; --orange: #f48022; --white: #ffffff; --dark-bg: #001427; /* Deep Navy */ /* Additional UI colors */ --dark-blue: #051e3e; /* Slightly lighter navy */ --light-blue: #2596be; /* Accent blue */ --light-gray: #f0f2f5; /* Off-white for light backgrounds if needed */ --medium-gray: #a0a8b1; /* Text subtle */ --dark-gray: #1e2a3a; /* Darker element backgrounds */ /* Functional colors */ --success: #34c759; --warning: #ffcc00; /* Brighter warning */ --error: #ff3b30; /* Typography */ --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; --font-mono: 'IBM Plex Mono', monospace; /* Spacing */ --space-xs: 0.25rem; /* 4px */ --space-sm: 0.5rem; /* 8px */ --space-md: 1rem; /* 16px */ --space-lg: 1.5rem; /* 24px */ --space-xl: 2.5rem; /* 40px */ --space-xxl: 4rem; /* 64px */ /* Border radius */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 20px; --radius-round: 50%; /* Shadows */ --shadow-sm: 0 2px 4px rgba(0, 20, 39, 0.15); --shadow-md: 0 5px 15px rgba(0, 20, 39, 0.2); --shadow-lg: 0 10px 30px rgba(0, 20, 39, 0.25); --shadow-glow-blue: 0 0 15px rgba(0, 164, 239, 0.3); --shadow-glow-magenta: 0 0 15px rgba(255, 0, 102, 0.3); /* Transitions */ --transition-fast: 0.2s ease; --transition-normal: 0.4s ease; /* Slightly slower for smoother effect */ --transition-slow: 0.6s ease; /* Z-index layers */ --z-background: -1; --z-base: 1; --z-content: 10; --z-sticky: 100; --z-navbar: 900; /* Below notification */ --z-notification: 1000; --z-modal: 1100; --z-tooltip: 1200; /* Notification Height - JS will set this */ --notification-height: 0px; } /* Reset & Base Styles */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; scroll-behavior: smooth; scroll-padding-top: 100px; /* Adjust for fixed navbar height */ } body { font-family: var(--font-body); background-color: var(--dark-bg); color: var(--medium-gray); /* Default text slightly softer */ line-height: 1.7; /* Increased line-height */ overflow-x: hidden; min-height: 100vh; display: flex; flex-direction: column; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; /* Needed for background elements */ } /* Main content area */ main { flex-grow: 1; /* Ensure footer is pushed down */ } h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); line-height: 1.3; font-weight: 700; margin-bottom: var(--space-md); color: var(--white); /* Headings are white */ } h1 { font-size: clamp(2.5rem, 5vw + 1rem, 3.8rem); margin-bottom: var(--space-lg); letter-spacing: -0.03em; } h2 { font-size: clamp(2rem, 4vw + 0.8rem, 2.8rem); margin-bottom: var(--space-lg); } h3 { font-size: clamp(1.3rem, 3vw + 0.5rem, 1.75rem); } h4 { font-size: clamp(1.1rem, 2.5vw + 0.4rem, 1.375rem); } p { margin-bottom: var(--space-lg); /* More space after paragraphs */ max-width: 70ch; /* Improve readability */ } a { color: var(--blue); text-decoration: none; transition: color var(--transition-fast); } a:hover { color: var(--light-blue); } img, svg { max-width: 100%; height: auto; display: block; /* Remove bottom space */ } button { font-family: inherit; cursor: pointer; } code, pre { font-family: var(--font-mono); background-color: var(--dark-gray); padding: 0.2em 0.4em; border-radius: var(--radius-sm); font-size: 0.9em;} pre { padding: var(--space-md); overflow-x: auto; } ul, ol { list-style-position: outside; padding-left: var(--space-lg); } li { margin-bottom: var(--space-sm); } /* Container */ .container { width: 100%; max-width: 1240px; /* Slightly wider */ margin: 0 auto; padding: 0 var(--space-lg); } /* Background effects */ .bg-gradient { position: fixed; /* Fixed position */ top: 0; left: 0; width: 100%; height: 100vh; /* Full viewport height */ overflow: hidden; z-index: var(--z-background); pointer-events: none; /* Allow clicks through */ } .bg-blob { position: absolute; border-radius: var(--radius-round); filter: blur(80px); /* More blur */ opacity: 0.25; /* More subtle */ will-change: transform; /* Optimize animation */ } .bg-blob-1 { width: 50vw; height: 50vw; min-width: 400px; min-height: 400px; max-width: 700px; max-height: 700px; top: -15%; right: -10%; background: radial-gradient(circle, var(--magenta) 0%, transparent 70%); animation: float 25s ease-in-out infinite alternate; } .bg-blob-2 { width: 45vw; height: 45vw; min-width: 350px; min-height: 350px; max-width: 600px; max-height: 600px; bottom: -10%; left: -10%; background: radial-gradient(circle, var(--blue) 0%, transparent 70%); animation: float 30s ease-in-out infinite alternate-reverse; } @keyframes float { 0% { transform: translate(0, 0) rotate(0deg) scale(1); } 100% { transform: translate(50px, -30px) rotate(25deg) scale(1.1); } } /* Navbar */ .navbar { position: fixed; top: var(--notification-height); /* Adjusted by JS */ left: 0; right: 0; background: rgba(0, 20, 39, 0.7); /* Semi-transparent */ backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid rgba(255, 255, 255, 0.08); z-index: var(--z-navbar); padding: var(--space-sm) 0; /* Consistent padding */ transition: background-color var(--transition-normal), box-shadow var(--transition-normal), padding var(--transition-normal), top var(--transition-fast); will-change: background-color, box-shadow, padding; } .navbar.scrolled { background: rgba(5, 30, 62, 0.9); /* Darker blue on scroll */ box-shadow: var(--shadow-md); /* padding: var(--space-xs) 0; Slightly reduced padding */ } .navbar-container { display: flex; align-items: center; justify-content: space-between; } .logo { display: flex; align-items: center; color: var(--white); /* Ensure logo text is white */ gap: var(--space-sm); } .logo img { height: 32px; transition: transform var(--transition-fast); } .logo:hover img { transform: rotate(-5deg) scale(1.05); } .logo-text { font-size: 1.3rem; font-weight: 700; } .nav-links { display: flex; align-items: center; list-style: none; margin: 0; padding: 0; } .nav-links li { margin: 0 var(--space-sm); } /* Reduced margin */ .nav-links a { color: var(--white); font-weight: 500; font-size: 0.95rem; position: relative; padding: var(--space-xs) var(--space-sm); /* Padding for larger hit area */ border-radius: var(--radius-sm); transition: background-color var(--transition-fast), color var(--transition-fast); } .nav-links a::after { content: ''; position: absolute; width: 0; height: 2px; bottom: -2px; /* Position slightly below */ left: 50%; transform: translateX(-50%); background: linear-gradient(90deg, var(--magenta), var(--blue)); transition: width var(--transition-normal); border-radius: 1px; } .nav-links a:hover, .nav-links a.active { background-color: rgba(255, 255, 255, 0.1); color: var(--white); } .nav-links a.active::after { width: 60%; } /* Underline for active link */ .nav-links a:hover::after { width: 80%; } /* Wider underline on hover */ .nav-buttons { display: flex; align-items: center; } .mobile-toggle { display: none; background: none; border: none; color: var(--white); font-size: 1.75rem; padding: 0.5rem; } /* Buttons */ .btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.8rem 1.8rem; /* Slightly adjusted padding */ border-radius: var(--radius-md); font-weight: 600; font-size: 0.95rem; transition: all var(--transition-fast); border: none; text-align: center; cursor: pointer; position: relative; overflow: hidden; /* For hover effects */ white-space: nowrap; } .btn-icon { margin-right: var(--space-sm); font-size: 1.1em; } .btn-primary { background: linear-gradient(135deg, var(--orange), var(--magenta)); /* Orange to Magenta gradient */ color: var(--white); box-shadow: 0 4px 10px rgba(255, 0, 102, 0.2); } .btn-primary:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 6px 15px rgba(255, 0, 102, 0.3); color: var(--white); } .btn-secondary { background: rgba(255, 255, 255, 0.1); color: var(--white); border: 1px solid rgba(255, 255, 255, 0.2); } .btn-secondary:hover { background: rgba(255, 255, 255, 0.15); border-color: rgba(255, 255, 255, 0.4); transform: translateY(-3px); color: var(--white); } .btn-sm { padding: 0.6rem 1.2rem; font-size: 0.875rem; } .btn-lg { padding: 1rem 2.2rem; font-size: 1.05rem; } /* Hero Section */ .hero { padding: calc(var(--space-xxl) * 2 + var(--notification-height)) 0 var(--space-xxl); /* More top padding, account for notification */ min-height: 70vh; display: flex; align-items: center; position: relative; overflow: hidden; } .hero-content { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xl); align-items: center; } .hero-headings { position: relative; z-index: var(--z-content); } .hero h1 { line-height: 1.2; } .hero h1 span.gradient-text { /* Style specifically for hero gradient */ background: linear-gradient(120deg, var(--magenta), var(--blue), var(--orange)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; background-size: 200% auto; animation: hero-gradient-animation 5s linear infinite; } @keyframes hero-gradient-animation { to { background-position: 200% center; } } .hero-subtitle { font-size: clamp(1.1rem, 2.5vw + 0.5rem, 1.3rem); margin-bottom: var(--space-xl); color: var(--medium-gray); max-width: 550px; } .hero-buttons { display: flex; flex-wrap: wrap; gap: var(--space-md); margin-bottom: var(--space-xl); } .hero-animation { position: relative; width: 100%; min-height: 450px; /* Ensure space for animation */ display: flex; align-items: center; justify-content: center; perspective: 1500px; /* Increased perspective */ } /* DNA Helix Animation */ .dna-helix { position: absolute; width: 250px; /* Wider */ height: 100%; /* Fill the container */ transform-style: preserve-3d; /* Removed perspective from here, placed on parent (.hero-animation) */ } .base-pair { position: absolute; width: 100%; height: 20px; /* Base height */ transform-style: preserve-3d; /* Animation applied in JS */ } .base { position: absolute; width: 28px; /* Slightly larger */ height: 28px; border-radius: var(--radius-round); display: flex; align-items: center; justify-content: center; font-size: 0.9rem; font-weight: bold; color: var(--dark-bg); /* Dark text on bases */ text-shadow: none; box-shadow: inset 0 0 5px rgba(0,0,0,0.2), 0 2px 5px rgba(0,0,0,0.3); /* Animation applied in JS */ } .left-base { left: 30px; /* Adjusted positioning */ background: linear-gradient(135deg, #ff4d88, var(--magenta)); /* Gradient base */ animation: pulse 2.5s ease-in-out infinite; filter: brightness(1.1); } .right-base { right: 30px; /* Adjusted positioning */ background: linear-gradient(135deg, #4dd0ff, var(--blue)); /* Gradient base */ animation: pulse 2.5s ease-in-out infinite 0.2s; /* Staggered animation */ filter: brightness(1.1); } .base-connector { position: absolute; left: 50%; top: 50%; width: 130px; /* Connects bases */ height: 4px; background: linear-gradient(90deg, rgba(255, 0, 102, 0.6), rgba(255, 255, 255, 0.4) 50%, rgba(0, 164, 239, 0.6) ); transform: translate(-50%, -50%); border-radius: 2px; box-shadow: 0 0 8px rgba(255, 255, 255, 0.2); } /* Keyframes for DNA animation */ @keyframes rotate { /* Global rotation for each pair */ 0% { transform: rotateY(var(--start-angle)) translateZ(50px) translateX(var(--x-offset)); } 100% { transform: rotateY(calc(var(--start-angle) + 360deg)) translateZ(50px) translateX(var(--x-offset)); } } @keyframes pulse { /* Base pulsing effect */ 0%, 100% { transform: scale(1); opacity: 0.9; filter: brightness(1.1); } 50% { transform: scale(1.1); opacity: 1; filter: brightness(1.3); } } /* Section styling */ .section { padding: var(--space-xxl) 0; position: relative; overflow: hidden; /* Contain elements for animation */ } .section-title { text-align: center; margin-bottom: var(--space-xl); /* Reduced bottom margin */ } .section-title h2 { margin-bottom: var(--space-sm); } .section-title p { max-width: 700px; margin: 0 auto var(--space-lg); /* Center paragraph */ font-size: 1.1rem; color: var(--medium-gray); } .section-bg-dark { background-color: var(--dark-blue); } .section-bg-gradient { background: linear-gradient(160deg, rgba(5, 30, 62, 0.9), rgba(0, 20, 39, 0.9) 70%); border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); } /* Cards */ .card { background: rgba(255, 255, 255, 0.03); /* More subtle */ border-radius: var(--radius-lg); padding: var(--space-lg); transition: all var(--transition-normal); border: 1px solid rgba(255, 255, 255, 0.1); height: 100%; /* Ensure cards in grid have same height */ display: flex; flex-direction: column; } .card:hover { transform: translateY(-8px) scale(1.02); /* More lift */ background: rgba(255, 255, 255, 0.06); box-shadow: var(--shadow-lg); border-color: rgba(255, 255, 255, 0.2); } .card-title { font-size: 1.4rem; margin-bottom: var(--space-sm); color: var(--white); } .card-icon { width: 55px; height: 55px; background: linear-gradient(135deg, var(--magenta), var(--blue)); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.6rem; color: var(--white); margin-bottom: var(--space-md); flex-shrink: 0; /* Prevent icon from shrinking */ box-shadow: var(--shadow-glow-blue); } .card p { flex-grow: 1; } /* Allow text to fill space */ .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--space-xl); align-items: stretch; /* Make cards same height */ } /* Feature List (Alternative Card Layout) */ .feature-list { display: flex; flex-direction: column; gap: var(--space-lg); } .feature-card { display: flex; align-items: flex-start; gap: var(--space-lg); padding: var(--space-lg); background: rgba(255, 255, 255, 0.04); border-radius: var(--radius-lg); transition: all var(--transition-normal); border: 1px solid rgba(255, 255, 255, 0.1); } .feature-card:hover { transform: translateX(5px); /* Shift right on hover */ background: rgba(255, 255, 255, 0.07); box-shadow: var(--shadow-md); border-left: 3px solid; /* Add left border on hover */ } .feature-card:hover.feature-icon-blue { border-left-color: var(--blue); } .feature-card:hover.feature-icon-magenta { border-left-color: var(--magenta); } .feature-card:hover.feature-icon-orange { border-left-color: var(--orange); } .feature-icon { min-width: 50px; /* Slightly smaller */ height: 50px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.4rem; flex-shrink: 0; } /* Distinct feature icon styles */ .feature-icon-blue { background: rgba(0, 164, 239, 0.1); color: var(--blue); box-shadow: 0 0 10px rgba(0, 164, 239, 0.2); } .feature-icon-magenta { background: rgba(255, 0, 102, 0.1); color: var(--magenta); box-shadow: 0 0 10px rgba(255, 0, 102, 0.2); } .feature-icon-orange { background: rgba(244, 128, 34, 0.1); color: var(--orange); box-shadow: 0 0 10px rgba(244, 128, 34, 0.2); } .feature-content h3 { margin-bottom: var(--space-xs); font-size: 1.3rem; color: var(--white); } .feature-content p { margin-bottom: 0; font-size: 0.95rem; } /* Stats Section */ .stats-section { padding: var(--space-xl) 0; text-align: center; position: relative; background: linear-gradient(180deg, var(--dark-bg) 0%, var(--dark-blue) 100%); /* Subtle vertical gradient */ border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-lg); text-align: center; max-width: 1000px; margin: 0 auto; } .stat-item { padding: var(--space-sm); } .stat-icon { font-size: 2rem; margin-bottom: var(--space-sm); color: var(--blue); /* Use brand blue for icons */ opacity: 0.8; } .stat-number { font-size: clamp(2.5rem, 6vw, 3.5rem); /* Responsive font size */ font-weight: 700; line-height: 1.1; margin-bottom: var(--space-xs); background: linear-gradient(135deg, var(--magenta), var(--light-blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .stat-label { color: var(--medium-gray); font-size: 0.95rem; font-weight: 500; } /* Steps Section (How it Works) */ .steps-container { position: relative; display: flex; flex-direction: column; gap: var(--space-xl); max-width: 800px; /* Constrain width */ margin: var(--space-xl) auto 0; /* Add top margin */ } .steps-container::before { /* Vertical connecting line */ content: ''; position: absolute; left: 29px; /* Aligned with center of icons */ top: 30px; /* Start below first icon */ bottom: 30px; /* End above last icon */ width: 4px; background: linear-gradient(to bottom, rgba(0, 164, 239, 0.1), rgba(0, 164, 239, 0.5), rgba(0, 164, 239, 0.1) ); border-radius: 2px; z-index: var(--z-base); } .step-item { position: relative; display: flex; align-items: flex-start; /* Align items to top */ gap: var(--space-lg); padding: var(--space-md); background: rgba(255, 255, 255, 0.03); border-radius: var(--radius-lg); border: 1px solid rgba(255, 255, 255, 0.08); z-index: var(--z-content); /* Above the line */ transition: background-color var(--transition-normal), border-color var(--transition-normal); } .step-item:hover { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.15); } .step-icon { width: 60px; height: 60px; flex-shrink: 0; background: linear-gradient(135deg, var(--blue), var(--light-blue)); border-radius: var(--radius-round); display: flex; align-items: center; justify-content: center; font-size: 1.8rem; color: var(--white); box-shadow: var(--shadow-glow-blue); z-index: var(--z-content); /* Ensure icon is above line */ } /* Alternate step icon colors */ .step-item:nth-child(1) .step-icon { background: linear-gradient(135deg, var(--orange), #ffb347); box-shadow: 0 0 15px rgba(244, 128, 34, 0.3); } .step-item:nth-child(3) .step-icon { background: linear-gradient(135deg, var(--magenta), #ff4d88); box-shadow: var(--shadow-glow-magenta); } .step-content h3 { margin-bottom: var(--space-xs); font-size: 1.4rem; color: var(--white); } .step-content p { margin-bottom: 0; font-size: 1rem; } /* Footer */ .footer { background: var(--dark-blue); padding: var(--space-xxl) 0 var(--space-lg); border-top: 1px solid rgba(255, 255, 255, 0.1); margin-top: auto; /* Push footer to bottom */ } .footer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* Responsive columns */ gap: var(--space-xl); margin-bottom: var(--space-xl); } .footer-brand img { height: 36px; margin-bottom: var(--space-md); } .footer-brand p { margin-bottom: var(--space-lg); color: var(--medium-gray); font-size: 0.9rem; max-width: 300px;} .footer-social { display: flex; gap: var(--space-md); } .social-link { width: 40px; height: 40px; border-radius: var(--radius-round); background: rgba(255, 255, 255, 0.08); display: flex; align-items: center; justify-content: center; color: var(--white); font-size: 1.1rem; transition: all var(--transition-fast); } .social-link:hover { background: var(--blue); /* Use brand color on hover */ transform: translateY(-3px) scale(1.05); color: var(--white); } .footer-nav h4 { margin-bottom: var(--space-md); color: var(--white); font-size: 1.1rem; font-weight: 600; } .footer-nav ul { list-style: none; padding: 0; margin: 0; } .footer-nav li { margin-bottom: var(--space-sm); } .footer-nav a { color: var(--medium-gray); font-size: 0.95rem; transition: color var(--transition-fast); } .footer-nav a:hover { color: var(--white); text-decoration: underline; } .footer-bottom { margin-top: var(--space-xl); padding-top: var(--space-lg); border-top: 1px solid rgba(255, 255, 255, 0.08); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--space-md); font-size: 0.85rem; color: var(--medium-gray); } .footer-copyright a { color: var(--medium-gray); text-decoration: underline; } .footer-copyright a:hover { color: var(--white); } .footer-links { display: flex; gap: var(--space-lg); } .footer-links a { color: var(--medium-gray); } .footer-links a:hover { color: var(--white); } /* Disclaimer Section Specific Styles */ .disclaimer-section { padding: var(--space-lg) 0; /* Reduced padding */ background: transparent !important; /* Ensure no background override */ } .disclaimer-content { max-width: 900px; /* Wider */ margin: 0 auto; background-color: rgba(0, 10, 20, 0.4); /* Darker, subtle background */ border-radius: var(--radius-md); padding: var(--space-lg); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); } .disclaimer-title { font-size: 0.9rem; margin-bottom: var(--space-sm); color: var(--medium-gray); /* Softer title color */ font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; opacity: 0.8; } .disclaimer-text { font-size: 0.75rem; /* Slightly larger */ opacity: 0.7; /* Slightly more visible */ line-height: 1.5; color: var(--medium-gray); } .disclaimer-text p { margin-bottom: var(--space-sm); } /* Less space between paragraphs */ .disclaimer-text strong { color: rgba(255, 255, 255, 0.8); font-weight: 600; } /* Utilities */ .text-center { text-align: center; } .mb-sm { margin-bottom: var(--space-sm); } .mb-md { margin-bottom: var(--space-md); } .mb-lg { margin-bottom: var(--space-lg); } .mb-xl { margin-bottom: var(--space-xl); } .mt-sm { margin-top: var(--space-sm); } .mt-md { margin-top: var(--space-md); } .mt-lg { margin-top: var(--space-lg); } .mt-xl { margin-top: var(--space-xl); } .gradient-text { background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } /* Scroll-based Animations */ .scroll-animate, .scroll-animate-left, .scroll-animate-right, .scroll-animate-scale { opacity: 0; transition: opacity 0.8s cubic-bezier(0.165, 0.84, 0.44, 1), transform 0.8s cubic-bezier(0.165, 0.84, 0.44, 1); will-change: opacity, transform; /* Performance hint */ } .scroll-animate { transform: translateY(40px); } .scroll-animate-left { transform: translateX(-50px); } .scroll-animate-right { transform: translateX(50px); } .scroll-animate-scale { transform: scale(0.9); } .scroll-animate.animate-in, .scroll-animate-left.animate-in, .scroll-animate-right.animate-in, .scroll-animate-scale.animate-in { opacity: 1; transform: translate(0, 0) scale(1); } /* Delay classes */ .scroll-animate-delay-1 { transition-delay: 0.15s; } .scroll-animate-delay-2 { transition-delay: 0.3s; } .scroll-animate-delay-3 { transition-delay: 0.45s; } /* Responsive styles */ @media screen and (max-width: 1024px) { .hero-content { grid-template-columns: 1fr; text-align: center; gap: var(--space-lg); } .hero-headings { order: 1; } /* Text below animation on smaller screens */ .hero-animation { order: 0; min-height: 350px; margin-bottom: var(--space-lg); } .hero-subtitle { margin-left: auto; margin-right: auto; } .hero-buttons { justify-content: center; } .dna-helix { width: 200px; /* Smaller helix */ } .base-connector { width: 100px; } .left-base { left: 25px; } .right-base { right: 25px; } .footer-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } /* Adjust footer grid */ .steps-container::before { left: 29px; } /* Adjust step line position if needed */ } @media screen and (max-width: 768px) { html { scroll-padding-top: 80px; /* Adjust for potentially smaller navbar */ } h1 { font-size: 2.8rem; } h2 { font-size: 2.1rem; } .navbar-container { position: relative; } .mobile-toggle { display: block; } .nav-links { position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; background: var(--dark-blue); /* Solid background for mobile menu */ padding: var(--space-lg) 0; max-height: 0; /* Start closed */ overflow: hidden; opacity: 0; visibility: hidden; transition: max-height 0.4s ease, opacity 0.4s ease, visibility 0.4s ease; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .nav-links.active { max-height: 500px; /* Open height */ opacity: 1; visibility: visible; } .nav-links li { margin: var(--space-sm) 0; width: 100%; text-align: center; } .nav-links a { display: block; padding: var(--space-md) 0; } .nav-links a::after { display: none; } /* No underline in mobile */ .nav-buttons { display: none; } /* Hide GitHub button in mobile nav area, could move it inside */ .hero { padding-top: calc(var(--space-xl) * 2 + var(--notification-height)); } /* Adjust hero padding */ .hero-buttons { flex-direction: column; width: 100%; max-width: 300px; margin-left: auto; margin-right: auto; } .card-grid { grid-template-columns: 1fr; } /* Stack cards */ .feature-card { flex-direction: column; align-items: center; text-align: center; } .feature-icon { margin-bottom: var(--space-md); } .stats-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-md); } /* 2 columns for stats */ .steps-container { padding-left: 0; max-width: 100%;} .steps-container::before { display: none; } /* Hide vertical line on mobile */ .step-item { flex-direction: column; align-items: center; text-align: center; } .step-icon { margin-bottom: var(--space-md); } .footer-grid { grid-template-columns: 1fr; gap: var(--space-xl); text-align: center;} .footer-brand { align-items: center; } .footer-social { justify-content: center; } .footer-nav h4 { margin-top: var(--space-lg); margin-bottom: var(--space-sm); } .footer-bottom { flex-direction: column; text-align: center; } .notification-banner { padding: 0.6rem 2.5rem 0.6rem 1rem; font-size: 0.85rem; line-height: 1.4; } .notification-banner .notification-link { margin: 0.2rem; } } @media screen and (max-width: 480px) { .container { padding: 0 var(--space-md); } h1 { font-size: 2.2rem; } h2 { font-size: 1.8rem; } .stats-grid { grid-template-columns: 1fr; } /* Stack stats fully */ .hero-buttons .btn { width: 100%; } /* Full width buttons */ .footer-bottom { font-size: 0.8rem; } .footer-links { gap: var(--space-md); } /* Less gap for footer links */ .notification-banner { font-size: 0.8rem; } .notification-banner .notification-close { font-size: 1.3rem; right: 0.5rem; width: 26px; height: 26px;} } -------------------------------------------------- File: ./web/.htaccess -------------------------------------------------- RewriteEngine On # If the request doesn't directly map to an existing file or directory, append .html RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^([^/.]+)$ $1.html [L] -------------------------------------------------- File: ./web/index.html -------------------------------------------------- DNAnalyzer - DNA Sequence Analysis with Machine Learning
🚨 DNAnalyzer v2.0 is coming soon! Check out the Roadmap and join our Discord for updates!

Revolutionizing DNA analysis with machine learning

Powerful, privacy-focused DNA analysis using advanced ML algorithms designed for both researchers and individuals.

0
GitHub Stars
0M+
DNA Sequences Analyzed
0+
Contributors
86
Discord Members

Why Choose DNAnalyzer?

Our cutting-edge technology provides unparalleled accuracy and insights while keeping your data secure.

Privacy-First Analysis

Your genetic data never leaves your device. All computation is performed locally using our advanced on-device ML models.

Advanced DNA Analysis

Identify open reading frames, high coverage regions, protein sequences, and transcription factor binding sites with precision.

Machine Learning Powered

Our cutting-edge neural networks provide insights and pattern recognition beyond traditional analytics capabilities.

How It Works

A simple three-step process to analyze your DNA sequences

1. Upload Your Sequence

Upload your DNA sequence file in FASTA or FASTQ format. Your data stays securely on your device.

2. Select Analysis Options

Choose from a variety of analysis modules, including ORF detection, GC content analysis, and promoter identification.

3. Get Comprehensive Results

Receive detailed insights instantly with visualizations and downloadable reports for your research.

Powerful Analysis Features

Comprehensive tools designed for researchers, students, and enthusiasts

Open Reading Frame Identification

Scans DNA for start and stop codons to identify potential protein-coding sequences (ORFs). Recognizes canonical start/stop codons to pinpoint putative proteins within a genome.

GC-Content Analysis

Calculates and visualizes GC content across the sequence, highlighting GC-rich regions often associated with gene density and regulatory elements.

Core Promoter Element Identification

Scans for known core promoter motifs (e.g., TATA box, BRE, INR, DPE). Detecting these elements helps locate potential gene start sites and regulatory regions.

Ready to Start Analyzing?

Join thousands of researchers and scientists using DNAnalyzer for their genetic analysis needs.

DNAnalyzer Disclaimer

Limitation of Liability: The DNAnalyzer tool is intended for research, educational, and informational purposes only. The machine learning-based DNA analysis provides computational predictions that should not be used for medical diagnosis, clinical interpretation, or treatment decisions. DNAnalyzer is not a substitute for professional medical advice, diagnosis, or treatment.

Analysis Limitations: DNAnalyzer's computational analysis is based on algorithmic predictions using machine learning models trained on public datasets. Results may vary in accuracy. The absence of identified features does not rule out their presence, and identification does not guarantee biological relevance.

Privacy Notice: While DNAnalyzer processes data locally, users should exercise caution with sensitive genetic information. We recommend against uploading identifiable human genomic data. DNAnalyzer is not responsible for privacy implications from user actions.

Research Use Only: Results should be experimentally validated before use in publications. Citations should acknowledge its computational nature and limitations.

No Warranty: DNAnalyzer is provided "as is" without warranty of any kind. The creators and contributors disclaim all warranties. In no event shall creators/contributors be liable for any claim, damages, or liability arising from the use of this software.

-------------------------------------------------- File: ./web/style.css.bak -------------------------------------------------- /* DNA Helix Animation */ .dna-helix { position: relative; width: 200px; height: 400px; margin: 0 auto; perspective: 1200px; transform-style: preserve-3d; } .base-pair { position: absolute; width: 100%; height: 20px; animation: rotate 8s linear infinite; transform-style: preserve-3d; } .base { position: absolute; width: 25px; height: 25px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; text-shadow: 0 0 3px rgba(0,0,0,0.5); box-shadow: 0 0 15px rgba(0,0,0,0.2); } .left-base { left: 35px; background: #FF0066; animation: pulse 3s ease-in-out infinite; filter: brightness(1.1); } .right-base { right: 35px; background: #00B7FF; animation: pulse 3s ease-in-out infinite; filter: brightness(1.1); } .base-connector { position: absolute; left: 50%; width: 90px; height: 3px; background: linear-gradient(90deg, rgba(255, 0, 102, 0.8), rgba(255, 255, 255, 0.3) 50%, rgba(0, 183, 255, 0.8) ); transform: translateX(-50%); box-shadow: 0 0 8px rgba(0,0,0,0.15); } @keyframes rotate { 0% { transform: rotateY(0deg) translateZ(20px); } 100% { transform: rotateY(360deg) translateZ(20px); } } @keyframes pulse { 0% { transform: scale(1); opacity: 0.9; filter: brightness(1.1); } 50% { transform: scale(1.15); opacity: 1; filter: brightness(1.3); } 100% { transform: scale(1); opacity: 0.9; filter: brightness(1.1); } } /* DNAnalyzer - Main Stylesheet * Global styles for all pages */ /* Notification Banner Styling */ .notification-banner { background: linear-gradient(135deg, #FF6F00, #FF9100); color: var(--white); padding: 0.75rem 1rem 0.75rem 0.75rem; text-align: center; font-size: 1rem; font-weight: 600; position: fixed; top: 0; left: 0; width: 100%; z-index: var(--z-navbar); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); animation: notifPulse 3s infinite; transition: transform 0.3s ease-in-out; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; } .notification-banner.hide-notification { transform: translateY(-100%); } .notification-banner.closed { display: none !important; } .notification-banner .notification-link { color: inherit; text-decoration: none; font-weight: 700; background-color: rgba(255, 255, 255, 0.2); padding: 0.25rem 0.5rem; border-radius: 4px; margin: 0 0.5rem; transition: all 0.3s ease; white-space: nowrap; } .notification-banner .notification-link:hover { color: #FFD700; text-shadow: 0 0 5px rgba(255, 215, 0, 0.5); } .notification-banner .notification-close { background: rgba(255, 255, 255, 0.1); border: none; color: var(--white); font-size: 1.25rem; line-height: 1; position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); cursor: pointer; transition: all 0.3s ease; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 50%; padding: 0; } .notification-banner .notification-close::before { content: "×"; display: block; position: relative; top: -1px; } .notification-banner .notification-close:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-50%) rotate(90deg); color: #FFD700; text-shadow: 0 0 5px rgba(255, 215, 0, 0.5); } :root { /* Brand colors from logo */ --magenta: #ff0066; --blue: #00a4ef; --orange: #f48022; --white: #ffffff; --dark-bg: #001427; /* Additional UI colors */ --dark-blue: #051e3e; --light-blue: #2596be; --light-gray: #f5f5f7; --medium-gray: #86868b; --dark-gray: #333333; /* Functional colors */ --success: #34c759; --warning: #ff9500; --error: #ff3b30; /* Typography */ --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; --font-mono: 'IBM Plex Mono', monospace; /* Spacing */ --space-xs: 0.25rem; --space-sm: 0.5rem; --space-md: 1rem; --space-lg: 1.5rem; --space-xl: 2rem; --space-xxl: 3rem; /* Border radius */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 20px; /* Shadows */ --shadow-sm: 0 2px 5px rgba(0, 0, 0, 0.1); --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15); --shadow-lg: 0 8px 20px rgba(0, 0, 0, 0.2); /* Transitions */ --transition-fast: 0.2s ease; --transition-normal: 0.3s ease; --transition-slow: 0.5s ease; /* Z-index layers */ --z-tooltip: 100; --z-navbar: 200; --z-modal: 300; } /* Reset & Base Styles */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; scroll-behavior: smooth; } body { font-family: var(--font-body); background-color: var(--dark-bg); color: var(--white); line-height: 1.6; overflow-x: hidden; min-height: 100vh; display: flex; flex-direction: column; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); line-height: 1.2; font-weight: 700; margin-bottom: var(--space-md); } h1 { font-size: 3.5rem; margin-bottom: var(--space-lg); } h2 { font-size: 2.5rem; margin-bottom: var(--space-lg); } h3 { font-size: 1.75rem; } h4 { font-size: 1.375rem; } p { margin-bottom: var(--space-md); color: rgba(255, 255, 255, 0.85); } a { color: var(--blue); text-decoration: none; transition: color var(--transition-fast); } a:hover { color: var(--light-blue); } img { max-width: 100%; height: auto; } button { font-family: var(--font-body); cursor: pointer; } code, pre { font-family: var(--font-mono); } ul, ol { padding-left: var(--space-lg); margin-bottom: var(--space-md); } /* Container */ .container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 var(--space-lg); } .container-sm { max-width: 800px; } .container-lg { max-width: 1400px; } /* Navbar */ .navbar { position: fixed; top: var(--notification-height, 0); left: 0; right: 0; background: var(--dark-bg); border-bottom: 1px solid rgba(255, 255, 255, 0.08); z-index: var(--z-navbar); padding: var(--space-md) 0; transition: all var(--transition-normal), top 0.3s ease-in-out; } .navbar.scrolled { background: var(--dark-blue); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); padding: var(--space-sm) 0; } .navbar-container { display: flex; align-items: center; justify-content: space-between; } .logo { display: flex; align-items: center; } .logo img { height: 36px; margin-right: var(--space-sm); } .logo-text { font-size: 1.25rem; font-weight: 700; color: var(--white); } .nav-links { display: flex; align-items: center; list-style: none; margin: 0; padding: 0; } .nav-links li { margin: 0 var(--space-md); } .nav-links a { color: var(--white); font-weight: 500; position: relative; padding: var(--space-xs) 0; } .nav-links a::after { content: ''; position: absolute; width: 0; height: 2px; bottom: 0; left: 0; background: linear-gradient(90deg, var(--magenta), var(--blue)); transition: width var(--transition-normal); } .nav-links a:hover::after, .nav-links a.active::after { width: 100%; } .nav-buttons { display: flex; align-items: center; } .mobile-toggle { display: none; background: none; border: none; color: var(--white); font-size: 1.5rem; } /* Buttons */ .btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem; border-radius: var(--radius-md); font-weight: 600; transition: all var(--transition-fast); border: none; text-align: center; cursor: pointer; } .btn-primary { background: linear-gradient(135deg, #FF7B00, #FFB347); color: var(--white); } .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 164, 239, 0.4); color: var(--white); } .btn-secondary { background: rgba(255, 255, 255, 0.1); color: var(--white); border: 1px solid rgba(255, 255, 255, 0.2); } .btn-secondary:hover { background: rgba(255, 255, 255, 0.15); transform: translateY(-2px); color: var(--white); } .btn-sm { padding: 0.5rem 1rem; font-size: 0.875rem; } .btn-lg { padding: 1rem 2rem; font-size: 1.125rem; } .btn-icon { margin-right: var(--space-xs); } .btn-github { background: #24292e; color: var(--white); } .btn-github:hover { background: #2c3440; color: var(--white); } .btn-discord { background: #5865F2; color: var(--white); } .btn-discord:hover { background: #4752c4; color: var(--white); } /* Hero section */ .hero { padding: 160px 0 100px; position: relative; overflow: hidden; } .hero-content { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xxl); align-items: center; } .hero-headings { position: relative; z-index: 2; } .hero h1 { margin-bottom: var(--space-md); letter-spacing: -0.02em; line-height: 1.1; } .hero h1 span { background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .hero-subtitle { font-size: 1.25rem; margin-bottom: var(--space-xl); color: rgba(255, 255, 255, 0.85); max-width: 500px; } .hero-buttons { display: flex; gap: var(--space-md); margin-bottom: var(--space-xl); } .hero-animation { position: relative; width: 100%; height: 500px; display: flex; align-items: center; justify-content: center; } /* DNA Helix Animation */ .dna-helix { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; perspective: 1200px; } .base-pair { position: absolute; width: 100%; height: 30px; display: flex; justify-content: center; align-items: center; animation: rotate3D 12s linear infinite; } @keyframes rotate3D { 0% { transform: rotateY(0deg); } 100% { transform: rotateY(360deg); } } .base { width: 35px; height: 35px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; position: absolute; box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); } .left-base { background: var(--magenta); left: calc(50% - 120px); animation: moveLeft 6s ease-in-out infinite; } .right-base { background: var(--blue); right: calc(50% - 120px); animation: moveRight 6s ease-in-out infinite; } .base-connector { width: 200px; height: 3px; background: linear-gradient(90deg, var(--magenta), var(--blue)); position: absolute; animation: stretch 6s ease-in-out infinite; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); } @keyframes moveLeft { 0%, 100% { transform: translateX(-12px); } 50% { transform: translateX(12px); } } @keyframes moveRight { 0%, 100% { transform: translateX(12px); } 50% { transform: translateX(-12px); } } @keyframes stretch { 0%, 100% { width: 200px; } 50% { width: 220px; } } /* Section styling */ .section { padding: var(--space-xxl) 0; position: relative; } .section-title { text-align: center; margin-bottom: var(--space-xxl); } .section-title h2 { margin-bottom: var(--space-md); } .section-title p { max-width: 700px; margin: 0 auto; font-size: 1.1rem; } .section-bg-dark { background-color: var(--dark-blue); } .section-bg-gradient { background: linear-gradient(135deg, rgba(5, 30, 62, 0.7), rgba(0, 20, 39, 0.7)); } /* Cards */ .card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); padding: var(--space-xl); transition: all var(--transition-normal); border: 1px solid rgba(255, 255, 255, 0.1); } .card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: var(--shadow-md); border-color: rgba(255, 255, 255, 0.15); } .card-title { font-size: 1.3rem; margin-bottom: var(--space-md); } .card-icon { width: 60px; height: 60px; background: linear-gradient(135deg, var(--magenta), var(--blue)); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: var(--white); margin-bottom: var(--space-md); } .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--space-xl); } /* Feature cards */ .feature-card { display: flex; align-items: flex-start; gap: var(--space-lg); padding: var(--space-xl); background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); transition: all var(--transition-normal); border: 1px solid rgba(255, 255, 255, 0.1); margin-bottom: var(--space-xl); } .feature-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: var(--shadow-md); border-color: rgba(255, 255, 255, 0.15); } .feature-icon { min-width: 60px; height: 60px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; } .feature-icon-blue { background: rgba(0, 164, 239, 0.15); color: var(--blue); border: 1px solid rgba(0, 164, 239, 0.3); } .feature-icon-magenta { background: rgba(255, 0, 102, 0.15); color: var(--magenta); border: 1px solid rgba(255, 0, 102, 0.3); } .feature-icon-orange { background: rgba(244, 128, 34, 0.15); color: var(--orange); border: 1px solid rgba(244, 128, 34, 0.3); } .feature-content h3 { margin-bottom: var(--space-sm); } /* Stats */ .stats-section { padding: var(--space-xxl) 0; text-align: center; position: relative; background: linear-gradient(135deg, rgba(5, 30, 62, 0.7), rgba(0, 20, 39, 0.7)); border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-xl); text-align: center; max-width: 1000px; margin: 0 auto; } .stat-item { padding: var(--space-md); } .stat-number { font-size: 3rem; font-weight: 700; margin-bottom: var(--space-xs); background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .stat-label { color: rgba(255, 255, 255, 0.8); font-size: 1rem; font-weight: 500; } /* Steps */ .steps-container { counter-reset: step; position: relative; } .step-item { position: relative; padding-left: 70px; margin-bottom: var(--space-xl); } .step-item::before { counter-increment: step; content: counter(step); position: absolute; left: 0; top: 0; width: 50px; height: 50px; background: linear-gradient(135deg, var(--magenta), var(--blue)); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.25rem; color: var(--white); } .step-item:not(:last-child)::after { content: ''; position: absolute; left: 25px; top: 50px; bottom: -40px; width: 2px; background: linear-gradient(to bottom, var(--blue), transparent); } .step-content h3 { margin-bottom: var(--space-sm); } /* Footer */ .footer { background: var(--dark-blue); padding: var(--space-xxl) 0 var(--space-md); border-top: 1px solid rgba(255, 255, 255, 0.1); } .footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--space-xl); } .footer-brand { display: flex; flex-direction: column; } .footer-brand img { height: 40px; margin-bottom: var(--space-md); } .footer-brand p { margin-bottom: var(--space-lg); color: rgba(255, 255, 255, 0.7); } .footer-social { display: flex; gap: var(--space-md); } .social-link { width: 36px; height: 36px; border-radius: 50%; background: rgba(255, 255, 255, 0.1); display: flex; align-items: center; justify-content: center; color: var(--white); transition: all var(--transition-fast); } .social-link:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-2px); color: var(--white); } .footer-nav h4 { margin-bottom: var(--space-lg); color: var(--white); font-size: 1.1rem; } .footer-nav ul { list-style: none; padding: 0; margin: 0; } .footer-nav li { margin-bottom: var(--space-sm); } .footer-nav a { color: rgba(255, 255, 255, 0.7); transition: color var(--transition-fast); } .footer-nav a:hover { color: var(--white); } .footer-bottom { margin-top: var(--space-xxl); padding-top: var(--space-md); border-top: 1px solid rgba(255, 255, 255, 0.1); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--space-md); } .footer-copyright { color: rgba(255, 255, 255, 0.6); font-size: 0.875rem; } .footer-links { display: flex; gap: var(--space-lg); } .footer-links a { color: rgba(255, 255, 255, 0.6); font-size: 0.875rem; } .footer-links a:hover { color: var(--white); } /* Background effects */ .bg-gradient { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: -1; } .bg-blob { position: absolute; border-radius: 50%; filter: blur(60px); opacity: 0.5; } .bg-blob-1 { top: -200px; right: -100px; width: 600px; height: 600px; background: linear-gradient(135deg, rgba(255, 0, 102, 0.2), rgba(0, 0, 0, 0)); animation: float 20s ease-in-out infinite alternate; } .bg-blob-2 { bottom: -150px; left: -100px; width: 500px; height: 500px; background: linear-gradient(135deg, rgba(0, 164, 239, 0.2), rgba(0, 0, 0, 0)); animation: float 15s ease-in-out infinite alternate-reverse; } @keyframes float { 0% { transform: translate(0, 0) rotate(0deg); } 100% { transform: translate(40px, 40px) rotate(15deg); } } /* Utilities */ .text-center { text-align: center; } .mb-sm { margin-bottom: var(--space-sm); } .mb-md { margin-bottom: var(--space-md); } .mb-lg { margin-bottom: var(--space-lg); } .mb-xl { margin-bottom: var(--space-xl); } .mt-sm { margin-top: var(--space-sm); } .mt-md { margin-top: var(--space-md); } .mt-lg { margin-top: var(--space-lg); } .mt-xl { margin-top: var(--space-xl); } .gradient-text { background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } /* Responsive styles */ @media screen and (max-width: 1024px) { h1 { font-size: 3rem; } h2 { font-size: 2.25rem; } .hero-content { grid-template-columns: 1fr; text-align: center; } .hero-subtitle { margin-left: auto; margin-right: auto; } .hero-buttons { justify-content: center; } .hero-animation { margin-top: var(--space-xl); } .footer-grid { grid-template-columns: 1fr 1fr; gap: var(--space-xl) var(--space-xxl); } } @media screen and (max-width: 768px) { h1 { font-size: 2.5rem; } h2 { font-size: 2rem; } .navbar-container { position: relative; } .nav-links { position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; background: var(--dark-bg); padding: var(--space-lg); transform: translateY(-100%); opacity: 0; visibility: hidden; transition: all var(--transition-normal); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .nav-links.active { transform: translateY(0); opacity: 1; visibility: visible; } .nav-links li { margin: var(--space-sm) 0; } .mobile-toggle { display: block; } .hero-buttons { flex-direction: column; width: 100%; max-width: 300px; margin-left: auto; margin-right: auto; } .feature-card { flex-direction: column; } .footer-grid { grid-template-columns: 1fr; gap: var(--space-xl); } .footer-bottom { flex-direction: column; align-items: center; text-align: center; } } @media screen and (max-width: 480px) { .container { padding: 0 var(--space-md); } h1 { font-size: 2rem; } h2 { font-size: 1.75rem; } .stats-grid { grid-template-columns: 1fr; } } @media screen and (max-width: 768px) { .notification-banner { padding: 0.5rem 2rem 0.5rem 0.5rem; font-size: 0.9rem; } .notification-banner span { display: inline; } .notification-banner .notification-link { margin: 0 0.25rem; } } @media screen and (max-width: 480px) { .notification-banner { padding: 0.5rem 2rem 0.5rem 0.5rem; font-size: 0.8rem; line-height: 1.4; } .notification-banner span { display: inline; } .notification-banner .notification-link { margin: 0.25rem; padding: 0.15rem 0.35rem; } } /* DNAnalyzer Disclaimer CSS Update */ .disclaimer-section { padding: 1rem 0; background: transparent !important; /* Override any dark background */ } .disclaimer-content { max-width: 90%; margin: 0 auto; background-color: rgba(30, 30, 40, 0.4); /* Subtle dark background */ border-radius: 8px; padding: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.1); } .disclaimer-title { font-size: 0.85rem; margin-bottom: 0.75rem; opacity: 0.75; color: rgba(255, 255, 255, 0.8); font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; } .disclaimer-text { font-size: 0.7rem; opacity: 0.65; line-height: 1.4; color: rgba(255, 255, 255, 0.7); } /* Futuristic Notification Effects */ @keyframes notifPulse { 0% { box-shadow: 0 0 5px #FF9100; } 50% { box-shadow: 0 0 20px #FF9100; } 100% { box-shadow: 0 0 5px #FF9100; } } /* JavaScript will add this class when scrolling down */ .hide-notification { transform: translateY(-100%); } /* Scroll-based animations */ .scroll-animate { opacity: 0; transform: translateY(30px); transition: opacity 0.8s ease, transform 0.8s ease; } .scroll-animate.animate-in { opacity: 1; transform: translateY(0); } .scroll-animate-delay-1 { transition-delay: 0.2s; } .scroll-animate-delay-2 { transition-delay: 0.4s; } .scroll-animate-delay-3 { transition-delay: 0.6s; } .scroll-animate-right { opacity: 0; transform: translateX(50px); transition: opacity 0.8s ease, transform 0.8s ease; } .scroll-animate-right.animate-in { opacity: 1; transform: translateX(0); } .scroll-animate-left { opacity: 0; transform: translateX(-50px); transition: opacity 0.8s ease, transform 0.8s ease; } .scroll-animate-left.animate-in { opacity: 1; transform: translateX(0); } .scroll-animate-scale { opacity: 0; transform: scale(0.8); transition: opacity 0.8s ease, transform 0.8s ease; } .scroll-animate-scale.animate-in { opacity: 1; transform: scale(1); } /* Interactive particles */ .particles-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0; overflow: hidden; pointer-events: none; } .floating-particle { position: absolute; background: rgba(255, 255, 255, 0.1); border-radius: 50%; pointer-events: none; transition: transform 0.3s ease; } -------------------------------------------------- Directory: about ================================================== File: ./web/about/about.js -------------------------------------------------- /* * DNAnalyzer About Page JavaScript * Copyright © 2025 Piyush Acharya */ document.addEventListener('DOMContentLoaded', function() { // Mobile navigation toggle const mobileToggle = document.getElementById('mobileToggle'); const navLinks = document.getElementById('navLinks'); if (mobileToggle && navLinks) { mobileToggle.addEventListener('click', function() { navLinks.classList.toggle('active'); mobileToggle.querySelector('i').classList.toggle('fa-bars'); mobileToggle.querySelector('i').classList.toggle('fa-times'); }); } // Navbar scroll behavior const navbar = document.getElementById('navbar'); window.addEventListener('scroll', function() { if (window.scrollY > 100) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }); // Timeline animation - Adding view animation const timelineItems = document.querySelectorAll('.timeline-item'); const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 }; const timelineObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = entry.target.classList.contains('timeline-item:nth-child(odd)') ? 'translateX(0)' : 'translateX(0)'; observer.unobserve(entry.target); } }); }, observerOptions); timelineItems.forEach(item => { item.style.opacity = '0'; item.style.transform = item.classList.contains('timeline-item:nth-child(odd)') ? 'translateX(-20px)' : 'translateX(20px)'; item.style.transition = 'all 0.7s ease-out'; timelineObserver.observe(item); }); // Handle Vision Hexagon Hover/Click Interaction const hexagons = document.querySelectorAll('.hexagon'); const visionDetails = document.querySelectorAll('.vision-detail'); // Set first vision detail as active by default if (visionDetails.length > 0) { visionDetails[0].classList.add('active'); } hexagons.forEach((hexagon, index) => { hexagon.addEventListener('mouseenter', () => { // Remove active class from all vision details visionDetails.forEach(detail => { detail.classList.remove('active'); }); // Add active class to corresponding vision detail if (visionDetails[index]) { visionDetails[index].classList.add('active'); } }); hexagon.addEventListener('click', () => { // Remove active class from all vision details visionDetails.forEach(detail => { detail.classList.remove('active'); }); // Add active class to corresponding vision detail if (visionDetails[index]) { visionDetails[index].classList.add('active'); // If on mobile, scroll to the detail if (window.innerWidth < 992) { visionDetails[index].scrollIntoView({ behavior: 'smooth', block: 'center' }); } } }); }); // Add some interactivity to stats animateStats(); }); /** * Animates the stat numbers with a counting effect */ function animateStats() { const statNumbers = document.querySelectorAll('.stat-number[data-count]'); const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 }; const statsObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const targetNumber = parseInt(entry.target.getAttribute('data-count'), 10); const duration = 2000; // Animation duration in milliseconds const startTime = performance.now(); function updateCounter(currentTime) { const elapsedTime = currentTime - startTime; const progress = Math.min(elapsedTime / duration, 1); const easeProgress = easeOutQuad(progress); const currentCount = Math.floor(easeProgress * targetNumber); entry.target.textContent = currentCount; if (progress < 1) { requestAnimationFrame(updateCounter); } else { entry.target.textContent = targetNumber; } } requestAnimationFrame(updateCounter); observer.unobserve(entry.target); } }); }, observerOptions); statNumbers.forEach(statNumber => { statsObserver.observe(statNumber); }); } /** * Easing function for smoother animation * @param {number} t - Progress value between 0 and 1 * @return {number} Eased value */ function easeOutQuad(t) { return t * (2 - t); } // Add a subtle parallax effect to the background blobs window.addEventListener('mousemove', function(e) { const blobs = document.querySelectorAll('.bg-blob'); blobs.forEach(blob => { const speed = blob.classList.contains('bg-blob-1') ? 0.03 : 0.02; const x = (window.innerWidth / 2 - e.clientX) * speed; const y = (window.innerHeight / 2 - e.clientY) * speed; blob.style.transform = `translate(${x}px, ${y}px)`; }); }); -------------------------------------------------- File: ./web/about/about.html -------------------------------------------------- About - DNAnalyzer

About DNAnalyzer

Revolutionizing DNA analysis through advanced machine learning and efficient computation

Our Mission

DNAnalyzer aims to democratize DNA analysis by providing powerful, accessible tools for researchers, clinicians, and scientists worldwide. We're committed to advancing genomic research through innovative technology and open collaboration.

Our Core Values

Innovation

Leveraging cutting-edge technology and machine learning to provide accurate, comprehensive DNA analysis capabilities.

Accessibility

Making advanced DNA analysis tools available to researchers and clinicians worldwide through an intuitive interface.

Privacy

Ensuring user data remains secure and private with our on-device processing approach and transparent practices.

Scientific Rigor

Maintaining the highest standards of accuracy and reliability through extensive testing and validation.

Our Technology

Advanced Analysis

Identifies proteins, amino acids, start and stop codons, high coverage regions, and regulatory elements with high accuracy.

Machine Learning

Utilizes advanced algorithms and ML models to process and analyze DNA sequences efficiently.

Scalable Platform

Built on modern technology stack ensuring fast processing of large sequences and reliable performance.

Privacy-First Design

All processing happens on your device, ensuring your genetic data remains private and secure.

Our Journey

The evolution of DNAnalyzer from concept to cutting-edge DNA analysis platform

2022

Project Inception

Piyush Acharya initiates DNAnalyzer as a response to privacy concerns and high costs in the field of personal genomics.

2023

Team Expansion

Collaborator @LimesKey joins, and the project attracts its first contributors from the computational biology community.

2023

Nonprofit Foundation

DNAnalyzer becomes a fiscally sponsored 501(c)(3) nonprofit organization (EIN: 81-2908499) to advance DNA analysis technology.

2024

ML Integration

Launch of machine learning components with the "DNAI" model, significantly enhancing analysis capabilities.

2024

Web Platform

Release of the comprehensive web interface, making DNA analysis accessible to researchers worldwide.

2025

Open Source Growth

Community expands to 46+ contributors, including scientists from leading research institutions around the world.

Meet Our Leadership

Piyush Acharya

Piyush Acharya

Founder & Lead Developer

Initiated DNAnalyzer to democratize genome analysis and make it accessible while ensuring privacy.

LimesKey

@LimesKey

Co-Lead & ML Specialist

Leads the machine learning initiatives and computational biology aspects of DNAnalyzer.

Open Source Community

46+ Contributors

Dedicated contributors from Microsoft Research, University of Macedonia, Northeastern University, and more.

141
GitHub Stars
46
Contributors
5
M+
DNA Sequences Analyzed
Global Impact
Users from 42 countries

The Future of DNAnalyzer

Neural Networks

Our next-generation neural networks will process genotyped data from consumer DNA tests with unprecedented accuracy.

DIAMOND Integration

Integration with the DIAMOND sequence aligner will deliver up to 20,000x faster sequence alignment compared to traditional methods.

Genomic Database

A high-performance SQL database architecture specially designed for genomic data integration across thousands of species.

Advanced Analysis

Upcoming modules for repeat element detection, CRISPR site identification, and comprehensive protein domain searches.

Join Our Community

Contribute to the future of DNA analysis technology and help us make genomic insights accessible to all.

-------------------------------------------------- File: ./web/about/about.css -------------------------------------------------- /* About Page Styles */ /* General section styling */ .section { padding: 5rem 0; } .section-dark { background: linear-gradient(135deg, rgba(5, 30, 62, 0.7), rgba(0, 20, 39, 0.7)); border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); } .section-header { text-align: center; margin-bottom: 3rem; position: relative; } .section-header h2 { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; display: inline-block; } .section-divider { width: 80px; height: 4px; background: linear-gradient(135deg, var(--magenta), var(--blue)); margin: 1rem auto; border-radius: 2px; } .section-subtitle { max-width: 700px; margin: 0 auto; font-size: 1.1rem; color: rgba(255, 255, 255, 0.8); } .section-content { max-width: 900px; margin: 0 auto; } /* Hero section */ .about-hero { padding: 9rem 0 5rem; text-align: center; position: relative; background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.1)); } .about-hero-content h1 { font-size: 3.5rem; margin-bottom: 1.5rem; } .about-hero-content p { font-size: 1.2rem; color: rgba(255, 255, 255, 0.9); max-width: 800px; margin: 0 auto; line-height: 1.8; } /* Mission section */ .mission-section { text-align: center; } .mission-text { font-size: 1.3rem; line-height: 1.8; max-width: 900px; margin: 0 auto; color: rgba(255, 255, 255, 0.9); } /* Values section */ .values-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2rem; margin-top: 2rem; } .value-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 2rem; text-align: center; transition: all 0.3s ease; } .value-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.15); } .value-icon { width: 70px; height: 70px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 1.5rem; background: linear-gradient(135deg, rgba(255, 0, 102, 0.15), rgba(0, 164, 239, 0.15)); border: 2px solid transparent; transition: all 0.3s ease; } .value-card:hover .value-icon { background: linear-gradient(135deg, rgba(255, 0, 102, 0.3), rgba(0, 164, 239, 0.3)); transform: scale(1.1); } .value-icon i { font-size: 1.8rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .value-card h3 { font-size: 1.5rem; margin-bottom: 1rem; color: var(--white); } .value-card p { color: rgba(255, 255, 255, 0.8); line-height: 1.6; } /* Technology section */ .tech-showcase { display: grid; grid-template-columns: 1fr 1.5fr; gap: 4rem; align-items: center; } .tech-image { text-align: center; } .tech-logo { max-width: 100%; height: auto; max-height: 300px; animation: pulse 3s infinite alternate; } @keyframes pulse { from { transform: scale(1); opacity: 0.8; } to { transform: scale(1.05); opacity: 1; } } .tech-features { display: grid; grid-template-columns: 1fr; gap: 1.5rem; } .tech-feature { display: flex; align-items: flex-start; gap: 1.5rem; background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; } .tech-feature:hover { transform: translateX(10px); background: rgba(255, 255, 255, 0.08); } .feature-icon { flex-shrink: 0; width: 50px; height: 50px; border-radius: 12px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, rgba(255, 0, 102, 0.2), rgba(0, 164, 239, 0.2)); transition: all 0.3s ease; } .tech-feature:hover .feature-icon { background: linear-gradient(135deg, rgba(255, 0, 102, 0.3), rgba(0, 164, 239, 0.3)); transform: scale(1.1); } .feature-icon i { font-size: 1.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .feature-content h3 { margin-top: 0; margin-bottom: 0.5rem; font-size: 1.2rem; } .feature-content p { margin: 0; color: rgba(255, 255, 255, 0.8); } /* Timeline section - completely rewritten */ .timeline-section { position: relative; padding: 5rem 0; } .vertical-timeline { position: relative; max-width: 1100px; margin: 0 auto; padding: 2rem 0; } .vertical-timeline::before { content: ''; position: absolute; width: 2px; height: 100%; background: linear-gradient(to bottom, var(--magenta), var(--blue)); left: 50%; transform: translateX(-50%); z-index: 1; } .timeline-milestone { display: flex; position: relative; margin-bottom: 3rem; } .milestone-year { position: absolute; left: calc(50% - 60px); transform: translateX(-50%); background: var(--dark-blue); color: var(--blue); font-weight: 600; z-index: 2; padding: 0.2rem 0; } .milestone-dot { position: absolute; width: 20px; height: 20px; border-radius: 50%; background: linear-gradient(135deg, var(--magenta), var(--blue)); top: 0; left: 50%; transform: translateX(-50%); z-index: 2; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); } .milestone-card { width: 40%; background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 1.5rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.1); margin-left: auto; margin-right: 60px; position: relative; transition: all 0.3s ease; } .milestone-right .milestone-card { margin-left: 60px; margin-right: auto; } .milestone-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); } .milestone-card h3 { margin-top: 0; margin-bottom: 0.5rem; font-size: 1.3rem; } .milestone-card p { margin-bottom: 0; color: rgba(255, 255, 255, 0.8); } /* Connecting lines from timeline to cards */ .milestone-card::before { content: ''; position: absolute; width: 20px; height: 2px; background: linear-gradient(to right, var(--magenta), var(--blue)); top: 10px; right: -20px; } .milestone-right .milestone-card::before { left: -20px; right: auto; } /* Team section */ .team-horizontal { display: flex; gap: 2rem; margin-bottom: 3rem; flex-wrap: wrap; } .team-card { display: flex; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; flex: 1; min-width: 300px; gap: 1.5rem; align-items: center; } .team-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.15); } .team-avatar { width: 100px; height: 100px; border-radius: 50%; overflow: hidden; background: linear-gradient(135deg, rgba(255, 0, 102, 0.1), rgba(0, 164, 239, 0.1)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .team-avatar img { width: 60%; height: auto; } .team-info { flex-grow: 1; } .team-info h3 { margin-top: 0; margin-bottom: 0.3rem; font-size: 1.4rem; } .team-role { color: var(--blue); font-weight: 500; margin-bottom: 0.5rem; font-size: 0.95rem; } .team-bio { color: rgba(255, 255, 255, 0.8); margin-bottom: 1rem; font-size: 0.95rem; line-height: 1.5; } .team-social { display: flex; gap: 0.8rem; } .social-icon { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.1); color: var(--white); transition: all 0.3s ease; } .social-icon:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-3px); } /* Community Card */ .community-card { background: linear-gradient(135deg, rgba(255, 0, 102, 0.1), rgba(0, 164, 239, 0.1)); border-radius: 16px; padding: 2.5rem; text-align: center; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } .community-card:hover { transform: translateY(-8px); box-shadow: 0 15px 40px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.2); } .community-icon { width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, var(--magenta), var(--blue)); margin: 0 auto 1.5rem; display: flex; align-items: center; justify-content: center; } .community-icon i { font-size: 2rem; color: white; } .community-card h3 { font-size: 1.8rem; margin-bottom: 0.5rem; } .community-count { font-size: 1.2rem; font-weight: 600; color: var(--blue); margin-bottom: 1rem; } .community-description { color: rgba(255, 255, 255, 0.8); margin-bottom: 1.5rem; line-height: 1.6; } .community-links { display: flex; justify-content: center; gap: 1rem; } /* Stats section */ .stats-section { padding: 4rem 0; background: linear-gradient(135deg, rgba(5, 30, 62, 0.8), rgba(0, 20, 39, 0.8)); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 2rem; text-align: center; } .stat-item { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 2rem 1rem; transition: all 0.3s ease; } .stat-item:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); } .stat-icon { width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, rgba(255, 0, 102, 0.2), rgba(0, 164, 239, 0.2)); display: flex; align-items: center; justify-content: center; margin: 0 auto 1rem; } .stat-icon i { font-size: 1.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .stat-number { font-size: 2.5rem; font-weight: 700; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 0.2rem; display: inline-block; } .stat-suffix { font-size: 1.8rem; font-weight: 700; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; display: inline-block; } .stat-label { color: var(--white); font-weight: 500; margin-bottom: 0.5rem; } .stat-description { color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; } /* Future Vision Section - completely rewritten */ .future-vision-section { padding: 6rem 0; background: linear-gradient(135deg, rgba(5, 30, 62, 0.5), rgba(0, 20, 39, 0.5)); position: relative; overflow: hidden; } .future-grid-container { position: relative; width: 100%; max-width: 1100px; height: 550px; margin: 0 auto; } .future-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 120px; height: 120px; border-radius: 50%; background: linear-gradient(135deg, rgba(255, 0, 102, 0.3), rgba(0, 164, 239, 0.3)); display: flex; align-items: center; justify-content: center; z-index: 5; box-shadow: 0 0 30px rgba(0, 164, 239, 0.4); } .center-logo { width: 60%; height: auto; animation: pulse-grow 3s infinite alternate; } @keyframes pulse-grow { from { transform: scale(1); opacity: 0.8; } to { transform: scale(1.2); opacity: 1; } } /* Diagonal lines */ .future-grid-container::before, .future-grid-container::after { content: ''; position: absolute; top: 50%; left: 50%; background: linear-gradient(135deg, rgba(255, 0, 102, 0.2), rgba(0, 164, 239, 0.2)); height: 2px; width: 70%; transform-origin: center; z-index: 1; } .future-grid-container::before { transform: translate(-50%, -50%) rotate(45deg); } .future-grid-container::after { transform: translate(-50%, -50%) rotate(-45deg); } .future-card { position: absolute; width: 240px; height: 150px; z-index: 2; } .future-card-inner { width: 100%; height: 100%; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 1.5rem; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; transition: all 0.3s ease; } .future-card-inner:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.2); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); } .future-icon { width: 50px; height: 50px; border-radius: 50%; background: rgba(255, 255, 255, 0.1); display: flex; align-items: center; justify-content: center; margin-bottom: 0.8rem; } .future-icon i { font-size: 1.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .future-card h3 { font-size: 1rem; margin: 0 0 0.5rem 0; color: var(--white); } .future-detail { display: none; margin-top: 0.5rem; } .future-card-inner:hover .future-detail { display: block; } .future-detail p { font-size: 0.85rem; color: rgba(255, 255, 255, 0.8); margin: 0; line-height: 1.4; } /* Card positioning exactly matching the image */ .top-left { top: 25%; left: 0; transform: translateY(-50%); } .top-right { top: 25%; right: 0; transform: translateY(-50%); } .bottom-left { bottom: 25%; left: 0; transform: translateY(50%); } .bottom-right { bottom: 25%; right: 0; transform: translateY(50%); } /* Connecting lines */ .hexagon-container::before, .hexagon-container::after { content: ''; position: absolute; top: 50%; left: 50%; width: 50%; height: 1px; background: linear-gradient(to right, rgba(255, 0, 102, 0.3), rgba(0, 164, 239, 0.3)); z-index: 1; } .hexagon-container::before { transform: translate(-50%, -50%) rotate(45deg); } .hexagon-container::after { transform: translate(-50%, -50%) rotate(-45deg); } .vision-details { display: flex; flex-direction: column; gap: 1.5rem; } .vision-detail { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; } .vision-detail.active { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.2); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); transform: translateY(-5px) translateX(10px); } .vision-detail h3 { display: flex; align-items: center; gap: 0.8rem; margin-top: 0; margin-bottom: 0.8rem; font-size: 1.3rem; } .vision-detail h3 i { font-size: 1.2rem; color: var(--blue); } .vision-detail p { color: rgba(255, 255, 255, 0.8); line-height: 1.6; margin: 0; } /* CTA Section */ .cta-section { text-align: center; padding: 5rem 0; } .cta-content h2 { font-size: 2.5rem; margin-bottom: 1rem; } .cta-content p { max-width: 600px; margin: 0 auto 2rem; font-size: 1.1rem; color: rgba(255, 255, 255, 0.9); } .cta-buttons { display: flex; justify-content: center; gap: 1rem; } /* Media Queries */ @media screen and (max-width: 1200px) { .vision-container { grid-template-columns: 1fr; gap: 2rem; } .vision-canvas { height: 400px; } } @media screen and (max-width: 992px) { .tech-showcase { grid-template-columns: 1fr; gap: 3rem; } /* Timeline responsive styles */ .vertical-timeline::before { left: 30px; } .timeline-milestone { margin-left: 30px; } .milestone-dot { left: 30px; @media screen and (max-width: 768px) { .about-hero-content h1 { font-size: 2.8rem; } .section-header h2 { font-size: 2rem; } .mission-text { font-size: 1.1rem; } .hexagon { width: 150px; height: 150px; } .hex-content { width: 130px; height: 130px; } .hex-icon { width: 40px; height: 40px; } .hex-content h3 { font-size: 0.85rem; } .cta-buttons { flex-direction: column; max-width: 300px; margin: 0 auto; } } @media screen and (max-width: 576px) { .about-hero-content h1 { font-size: 2.3rem; } .about-hero-content p { font-size: 1rem; } .section { padding: 3rem 0; } .timeline-content { padding: 1rem; } .timeline-item { padding-left: 4rem; } .timeline-item:nth-child(odd) { padding-left: 4rem; } .stats-grid { grid-template-columns: 1fr; } .vision-canvas { height: 350px; } .hexagon-center { width: 100px; height: 100px; } .hexagon { width: 120px; height: 120px; } .hex-content { width: 100px; height: 100px; } } -------------------------------------------------- Directory: analyzer ================================================== File: ./web/analyzer/analyzer.css -------------------------------------------------- /* Analyzer Page Specific Styles */ /* Main layout */ .analyzer-main { padding-top: 100px; min-height: calc(100vh - 100px); } .analyzer-header { text-align: center; margin-bottom: var(--space-xl); } .analyzer-header h1 { margin-bottom: var(--space-md); } .analyzer-header p { font-size: 1.1rem; max-width: 600px; margin: 0 auto; color: rgba(255, 255, 255, 0.8); } /* Dashboard layout */ .analyzer-dashboard { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-xl); margin-bottom: var(--space-xxl); } /* Sidebar */ .dashboard-sidebar { position: sticky; top: 100px; height: fit-content; } .sidebar-section { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); padding: var(--space-lg); margin-bottom: var(--space-lg); border: 1px solid rgba(255, 255, 255, 0.1); } .sidebar-section h3 { font-size: 1.1rem; margin-bottom: var(--space-md); color: var(--white); } /* History list */ .history-list { background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-sm); } .history-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-lg); color: rgba(255, 255, 255, 0.5); text-align: center; } .history-empty i { font-size: 1.5rem; margin-bottom: var(--space-sm); } .history-item { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-sm); background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); margin-bottom: var(--space-xs); cursor: pointer; transition: all var(--transition-fast); } .history-item:hover { background: rgba(255, 255, 255, 0.1); } .history-item i { color: var(--blue); font-size: 1rem; } .history-details { flex: 1; overflow: hidden; } .history-name { font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--white); } .history-time { font-size: 0.8rem; color: rgba(255, 255, 255, 0.5); } /* Quick actions */ .quick-actions { display: flex; flex-direction: column; gap: var(--space-sm); } .sidebar-btn { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-md); background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); color: var(--white); font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all var(--transition-fast); } .sidebar-btn:hover { background: rgba(255, 255, 255, 0.12); transform: translateY(-2px); } .sidebar-btn i { color: var(--blue); } /* API Status */ .api-status-container { margin-bottom: 0; } .status-indicator { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-md); background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); } .status-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--medium-gray); } .status-dot.pulse { animation: pulse 1.5s infinite; } .status-dot.online { background: var(--success); } .status-dot.offline { background: var(--error); } @keyframes pulse { 0% { transform: scale(0.9); opacity: 0.7; } 50% { transform: scale(1.1); opacity: 1; } 100% { transform: scale(0.9); opacity: 0.7; } } /* Dashboard content */ .dashboard-content { min-height: 500px; } /* Tabs */ .analyzer-tabs { display: flex; gap: var(--space-md); margin-bottom: var(--space-lg); } .tab-btn { flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--space-sm); padding: var(--space-md); background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); color: rgba(255, 255, 255, 0.8); font-size: 1rem; font-weight: 500; cursor: pointer; transition: all var(--transition-fast); } .tab-btn:hover { background: rgba(255, 255, 255, 0.08); } .tab-btn.active { background: linear-gradient(135deg, rgba(255, 0, 102, 0.15), rgba(0, 164, 239, 0.15)); border-color: transparent; border-bottom: 2px solid var(--blue); color: var(--white); } .tab-btn i { color: var(--blue); } .tab-content { display: none; } .tab-content.active { display: block; } /* File upload */ .analyzer-upload { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-xxl) var(--space-md); background: rgba(255, 255, 255, 0.05); border: 2px dashed rgba(255, 255, 255, 0.15); border-radius: var(--radius-lg); text-align: center; cursor: pointer; transition: all var(--transition-normal); margin-bottom: var(--space-xl); } .analyzer-upload:hover { background: rgba(255, 255, 255, 0.08); border-color: var(--blue); transform: translateY(-2px); } .analyzer-upload.drag-over { background: rgba(0, 164, 239, 0.1); border-color: var(--blue); } .analyzer-upload i { font-size: 3rem; color: var(--blue); margin-bottom: var(--space-md); } .analyzer-upload h3 { margin-bottom: var(--space-sm); color: var(--white); } .analyzer-upload p { margin-bottom: var(--space-lg); color: rgba(255, 255, 255, 0.7); max-width: 400px; } .upload-formats { font-size: 0.8rem; color: rgba(255, 255, 255, 0.5); } /* Batch upload */ .batch-upload { margin-bottom: var(--space-xl); } .batch-files { display: flex; flex-direction: column; gap: var(--space-md); margin: var(--space-lg) 0; } .batch-file { display: flex; align-items: center; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-md); } .batch-status { margin-left: auto; font-size: 0.9rem; padding: var(--space-xs) var(--space-sm); border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.1); } .batch-status.queued { background: rgba(255, 255, 255, 0.15); } .batch-status.processing { background: rgba(0, 164, 239, 0.2); color: var(--blue); } .batch-status.completed { background: rgba(40, 167, 69, 0.2); color: var(--success); } .batch-status.error { background: rgba(220, 53, 69, 0.2); color: var(--error); } .batch-progress { margin-top: var(--space-lg); } .progress-container { width: 100%; height: 8px; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-sm); overflow: hidden; margin-bottom: var(--space-sm); } .progress-bar { height: 100%; background: var(--blue); transition: width 0.3s ease; } .progress-info { display: flex; justify-content: space-between; font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); } /* Genetic testing forms */ .genetic-testing-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: var(--space-lg); margin-bottom: var(--space-xl); } .form-section { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); padding: var(--space-lg); border: 1px solid rgba(255, 255, 255, 0.1); } .form-section h3 { margin-bottom: var(--space-md); font-size: 1.1rem; } .form-group { margin-bottom: var(--space-md); } .form-group label { display: block; margin-bottom: var(--space-xs); font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); } .form-control { width: 100%; padding: var(--space-sm) var(--space-md); background: rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); color: var(--white); font-size: 0.9rem; transition: all var(--transition-fast); } .form-control:focus { border-color: var(--blue); outline: none; } .form-select { height: 42px; } .testing-options { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-md); margin-top: var(--space-lg); } .test-option { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); padding: var(--space-md); border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-fast); cursor: pointer; } .test-option:hover { background: rgba(255, 255, 255, 0.08); transform: translateY(-2px); } .test-option.selected { border-color: var(--blue); background: rgba(0, 164, 239, 0.1); } .test-option-header { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-sm); } .test-option-header i { color: var(--blue); } .test-option p { font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); } /* File preview */ .file-preview { display: flex; align-items: center; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-md); margin-bottom: var(--space-lg); width: 100%; max-width: 500px; } .file-icon { font-size: 2rem; color: var(--blue); margin-right: var(--space-md); } .file-info { flex: 1; } .file-name { font-weight: 500; color: var(--white); margin-bottom: var(--space-xs); } .file-size { font-size: 0.8rem; color: rgba(255, 255, 255, 0.5); } .file-remove { color: rgba(255, 255, 255, 0.5); background: none; border: none; cursor: pointer; padding: var(--space-xs); border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: all var(--transition-fast); } .file-remove:hover { color: var(--white); background: rgba(255, 255, 255, 0.1); } /* Analysis options */ .analysis-options { margin-bottom: var(--space-xl); } .analysis-options > h3 { margin-bottom: var(--space-lg); font-size: 1.2rem; } .options-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--space-lg); margin-bottom: var(--space-xl); } .option-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-lg); padding: var(--space-lg); transition: all var(--transition-normal); } .option-card:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.15); transform: translateY(-2px); } .option-header { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-md); } .option-header i { color: var(--blue); font-size: 1.1rem; } .option-header h4 { margin: 0; font-size: 1.1rem; color: var(--white); } .option-content { display: flex; flex-direction: column; gap: var(--space-md); } /* Custom checkbox */ .option-checkbox { position: relative; display: flex; align-items: center; gap: var(--space-md); cursor: pointer; padding: var(--space-xs) 0; padding-left: 36px; /* Add left padding to make room for the checkmark */ user-select: none; color: rgba(255, 255, 255, 0.8); transition: all var(--transition-fast); } .option-checkbox:hover { color: var(--white); } .option-checkbox input { position: absolute; opacity: 0; cursor: pointer; } .checkmark { position: absolute; left: 0; top: 50%; /* Center vertically */ transform: translateY(-50%); /* Center vertically */ width: 20px; height: 20px; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: var(--radius-sm); transition: all var(--transition-fast); } .option-checkbox:hover .checkmark { background: rgba(255, 255, 255, 0.15); } .option-checkbox input:checked ~ .checkmark { background: var(--blue); border-color: var(--blue); } .checkmark:after { content: ""; position: absolute; display: none; } .option-checkbox input:checked ~ .checkmark:after { display: block; } .option-checkbox .checkmark:after { left: 7px; top: 3px; width: 5px; height: 10px; border: solid white; border-width: 0 2px 2px 0; transform: rotate(45deg); } /* Analyze button */ .analyze-btn { width: 100%; padding: var(--space-lg); background: linear-gradient(135deg, var(--magenta), var(--blue)); border: none; border-radius: var(--radius-md); color: var(--white); font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all var(--transition-fast); display: flex; align-items: center; justify-content: center; gap: var(--space-sm); } .analyze-btn:hover { background: linear-gradient(135deg, var(--blue), var(--magenta)); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } .analyze-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; } /* Results section */ .analyzer-results { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: var(--space-xxl); } .results-header { display: flex; align-items: center; justify-content: space-between; padding: var(--space-lg); border-bottom: 1px solid rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.2); } .results-header h2 { margin: 0; font-size: 1.3rem; } .results-actions { display: flex; align-items: center; gap: var(--space-lg); } .results-file-info { display: flex; align-items: center; gap: var(--space-sm); color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; } .results-content { padding: var(--space-xl); } /* Results grid layout */ .results-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-lg); margin-bottom: var(--space-xl); } .result-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); padding: var(--space-lg); text-align: center; transition: all var(--transition-normal); } .result-card:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.15); transform: translateY(-2px); } .result-value { font-size: 2rem; font-weight: 700; color: var(--blue); margin-bottom: var(--space-xs); } .result-label { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); } .result-card.wide { grid-column: 1 / -1; } /* Charts and visualizations */ .chart-container { background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-lg); margin-bottom: var(--space-xl); } .base-composition { display: flex; align-items: center; margin-bottom: var(--space-lg); } .base-bar { flex: 1; height: 32px; display: flex; border-radius: var(--radius-md); overflow: hidden; } .base-segment { height: 100%; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 0.9rem; transition: all var(--transition-normal); } .base-segment:hover { filter: brightness(1.1); } .base-a { background-color: var(--magenta); } .base-t { background-color: var(--blue); } .base-g { background-color: var(--orange); } .base-c { background-color: #34c759; } .base-legend { display: flex; flex-wrap: wrap; gap: var(--space-md); } .legend-item { display: flex; align-items: center; gap: var(--space-xs); font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); } .color-box { width: 12px; height: 12px; border-radius: 2px; } /* Results table */ .results-table-container { margin-bottom: var(--space-xl); } .results-table-container h3 { margin-bottom: var(--space-md); font-size: 1.2rem; } .results-table { width: 100%; border-collapse: collapse; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); overflow: hidden; } .results-table th { text-align: left; padding: var(--space-md); background: rgba(0, 0, 0, 0.3); color: var(--blue); font-weight: 600; font-size: 0.9rem; } .results-table td { padding: var(--space-md); border-top: 1px solid rgba(255, 255, 255, 0.05); color: rgba(255, 255, 255, 0.8); font-size: 0.9rem; } .results-table tr:hover td { background: rgba(255, 255, 255, 0.05); } .sequence-cell { font-family: var(--font-mono); font-size: 0.85rem; } /* Sequence Visualization */ .sequence-visualization { margin-bottom: var(--space-xl); background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-lg); overflow: hidden; } .sequence-visualization h3 { margin-bottom: var(--space-md); } .sequence-view { display: grid; grid-template-columns: repeat(auto-fill, minmax(24px, 1fr)); gap: 2px; margin-bottom: var(--space-md); font-family: var(--font-mono); font-size: 0.8rem; } .base-block { width: 100%; aspect-ratio: 1; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; border-radius: 2px; transition: all var(--transition-fast); } .base-block:hover { transform: scale(1.1); z-index: 10; } .sequence-zoom { margin-top: var(--space-md); display: flex; justify-content: center; gap: var(--space-md); } .zoom-btn { background: rgba(255, 255, 255, 0.1); border: none; color: var(--white); width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all var(--transition-fast); } .zoom-btn:hover { background: rgba(255, 255, 255, 0.2); } .zoom-btn:disabled { opacity: 0.5; cursor: not-allowed; } .sequence-position { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); text-align: center; margin-top: var(--space-sm); } /* Circular Visualization */ .circular-genome { display: flex; justify-content: center; align-items: center; margin: var(--space-lg) 0; position: relative; } .circular-genome svg { max-width: 100%; height: auto; } .genome-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-md); margin-top: var(--space-md); } /* Codon Distribution */ .codon-distribution { margin-bottom: var(--space-xl); } .codon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: var(--space-md); margin-top: var(--space-md); } .codon-box { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-sm); padding: var(--space-sm); text-align: center; border: 1px solid rgba(255, 255, 255, 0.1); } .codon-box-name { font-family: var(--font-mono); font-weight: 600; color: var(--white); margin-bottom: var(--space-xs); } .codon-count { font-size: 0.85rem; color: var(--blue); } .codon-amino { font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); } /* 3D Protein Structure */ .protein-structure { width: 100%; height: 400px; background: rgba(0, 0, 0, 0.3); border-radius: var(--radius-md); margin-bottom: var(--space-xl); position: relative; overflow: hidden; } .structure-controls { position: absolute; bottom: var(--space-md); right: var(--space-md); display: flex; gap: var(--space-xs); } .structure-control-btn { width: 36px; height: 36px; border-radius: 50%; background: rgba(0, 0, 0, 0.5); color: white; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all var(--transition-fast); } .structure-control-btn:hover { background: rgba(0, 0, 0, 0.7); } /* Genetic Health Risk Display */ .health-risk-section { margin-bottom: var(--space-xl); } .risk-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-lg); margin-top: var(--space-lg); } .risk-card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); padding: var(--space-lg); border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-normal); } .risk-card:hover { transform: translateY(-3px); border-color: rgba(255, 255, 255, 0.2); } .risk-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-md); } .risk-title { font-size: 1.1rem; font-weight: 600; color: var(--white); } .risk-badge { padding: var(--space-xs) var(--space-sm); border-radius: var(--radius-sm); font-size: 0.8rem; font-weight: 600; } .risk-badge.low { background: rgba(40, 167, 69, 0.2); color: var(--success); } .risk-badge.moderate { background: rgba(255, 193, 7, 0.2); color: var(--warning); } .risk-badge.elevated { background: rgba(255, 105, 0, 0.2); color: var(--orange); } .risk-badge.high { background: rgba(220, 53, 69, 0.2); color: var(--error); } .risk-genes { margin-top: var(--space-md); font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); } .risk-genes span { display: inline-block; font-family: var(--font-mono); background: rgba(0, 0, 0, 0.3); padding: 0 var(--space-xs); border-radius: var(--radius-sm); margin-right: var(--space-xs); } .risk-description { margin-top: var(--space-md); font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); line-height: 1.5; } /* Ancestry Composition */ .ancestry-map { width: 100%; height: 400px; background: rgba(0, 0, 0, 0.3); border-radius: var(--radius-md); margin-bottom: var(--space-lg); position: relative; overflow: hidden; } .ancestry-chart { display: flex; margin-top: var(--space-lg); margin-bottom: var(--space-lg); } .ancestry-donut { width: 200px; height: 200px; position: relative; } .ancestry-categories { flex: 1; margin-left: var(--space-xl); } .ancestry-category { display: flex; align-items: center; margin-bottom: var(--space-sm); } .ancestry-color { width: 16px; height: 16px; border-radius: 2px; margin-right: var(--space-sm); } .ancestry-label { flex: 1; font-size: 0.9rem; color: rgba(255, 255, 255, 0.9); } .ancestry-percent { font-size: 0.9rem; font-weight: 600; color: var(--white); } /* Trait Analysis */ .traits-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-lg); margin-top: var(--space-lg); } .trait-card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); padding: var(--space-lg); border: 1px solid rgba(255, 255, 255, 0.1); } .trait-header { display: flex; align-items: center; gap: var(--space-md); margin-bottom: var(--space-md); } .trait-icon { font-size: 1.5rem; color: var(--blue); } .trait-name { font-size: 1.1rem; font-weight: 500; color: var(--white); } .trait-value { margin-top: var(--space-sm); font-size: 1rem; color: var(--white); } .trait-probability { display: flex; align-items: center; margin-top: var(--space-md); } .probability-bar { flex: 1; height: 8px; background: rgba(0, 0, 0, 0.3); border-radius: var(--radius-sm); overflow: hidden; margin-right: var(--space-sm); } .probability-fill { height: 100%; background: var(--blue); } .probability-value { font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); } /* Batch results */ .batch-results-list { display: flex; flex-direction: column; gap: var(--space-md); margin-bottom: var(--space-xl); } .batch-result-item { display: flex; align-items: center; background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); padding: var(--space-md); border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-fast); } .batch-result-item:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.2); } .batch-result-icon { font-size: 1.3rem; color: var(--blue); margin-right: var(--space-md); } .batch-result-details { flex: 1; } .batch-result-name { font-weight: 500; color: var(--white); margin-bottom: var(--space-xs); } .batch-result-stats { display: flex; gap: var(--space-lg); font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); } .batch-actions { display: flex; gap: var(--space-xs); } .batch-action-btn { width: 36px; height: 36px; border-radius: 50%; background: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.7); border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all var(--transition-fast); } .batch-action-btn:hover { background: rgba(255, 255, 255, 0.15); color: var(--white); } /* Compare view */ .compare-container { background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-lg); margin-bottom: var(--space-xl); } .compare-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-lg); padding-bottom: var(--space-md); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .compare-files { display: flex; gap: var(--space-md); } .compare-file { font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); padding: var(--space-xs) var(--space-sm); background: rgba(255, 255, 255, 0.08); border-radius: var(--radius-sm); } .compare-file.active { background: rgba(0, 164, 239, 0.15); color: var(--blue); } .compare-table { width: 100%; } .compare-table th { text-align: left; padding: var(--space-md); background: rgba(0, 0, 0, 0.3); color: var(--blue); font-weight: 600; font-size: 0.9rem; } .compare-table th:first-child { background: transparent; color: rgba(255, 255, 255, 0.8); } .compare-table td { padding: var(--space-md); border-top: 1px solid rgba(255, 255, 255, 0.05); color: rgba(255, 255, 255, 0.8); font-size: 0.9rem; } .compare-table tr:nth-child(odd) td { background: rgba(255, 255, 255, 0.02); } /* Nucleotide Distribution Hexplot */ .nucleotide-hexplot { display: flex; justify-content: center; margin: var(--space-lg) 0; } /* Histogram */ .histogram-container { margin: var(--space-lg) 0; } .histogram-bars { display: flex; align-items: flex-end; height: 200px; gap: 2px; } .histogram-bar { flex: 1; background: var(--blue); min-width: 4px; border-radius: 2px 2px 0 0; transition: all var(--transition-fast); } .histogram-bar:hover { background: var(--magenta); } .histogram-labels { display: flex; justify-content: space-between; margin-top: var(--space-xs); font-size: 0.8rem; color: rgba(255, 255, 255, 0.7); } /* Download options */ .download-options { background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-lg); } .download-options h3 { margin-bottom: var(--space-md); font-size: 1.1rem; } .download-formats { display: flex; gap: var(--space-md); flex-wrap: wrap; } .download-format { display: flex; align-items: center; gap: var(--space-xs); padding: var(--space-sm) var(--space-md); background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); color: var(--white); font-size: 0.9rem; cursor: pointer; transition: all var(--transition-fast); } .download-format:hover { background: rgba(255, 255, 255, 0.12); transform: translateY(-2px); } /* Loading state */ .loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-xxl); } .loading-spinner { width: 50px; height: 50px; border: 3px solid rgba(255, 255, 255, 0.1); border-radius: 50%; border-top: 3px solid var(--blue); animation: spin 1s linear infinite; margin-bottom: var(--space-md); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Coming soon */ .coming-soon { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-xxl); text-align: center; } .coming-soon i { font-size: 3rem; color: var(--blue); margin-bottom: var(--space-lg); opacity: 0.8; } .coming-soon h3 { margin-bottom: var(--space-md); color: var(--white); } .coming-soon p { max-width: 500px; color: rgba(255, 255, 255, 0.7); } /* Modal */ .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: var(--z-modal); opacity: 0; visibility: hidden; transition: all var(--transition-normal); } .modal.active { opacity: 1; visibility: visible; } .modal-content { background: var(--dark-bg); border-radius: var(--radius-lg); width: 90%; max-width: 500px; box-shadow: var(--shadow-lg); overflow: hidden; transform: translateY(20px); transition: all var(--transition-normal); } .modal.active .modal-content { transform: translateY(0); } .modal-header { display: flex; align-items: center; justify-content: space-between; padding: var(--space-lg); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .modal-header h3 { margin: 0; font-size: 1.2rem; } .close-modal { background: none; border: none; color: rgba(255, 255, 255, 0.7); font-size: 1.5rem; cursor: pointer; transition: all var(--transition-fast); } .close-modal:hover { color: var(--white); } .modal-body { padding: var(--space-lg); } /* Export options */ .export-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-md); margin-top: var(--space-lg); } .export-option { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-lg); background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); cursor: pointer; transition: all var(--transition-fast); } .export-option:hover { background: rgba(255, 255, 255, 0.08); transform: translateY(-3px); } .export-option i { font-size: 2rem; margin-bottom: var(--space-sm); color: var(--blue); } /* Notification */ .notification-container { position: fixed; bottom: var(--space-lg); right: var(--space-lg); z-index: var(--z-tooltip); display: flex; flex-direction: column; gap: var(--space-sm); } .notification { display: flex; align-items: center; padding: var(--space-md) var(--space-lg); background: rgba(0, 0, 0, 0.8); border-left: 3px solid var(--blue); border-radius: var(--radius-md); box-shadow: var(--shadow-md); min-width: 280px; max-width: 400px; transform: translateX(100%); opacity: 0; animation: slide-in 0.3s forwards; } .notification.success { border-left-color: var(--success); } .notification.success i { color: var(--success); } .notification.error { border-left-color: var(--error); } .notification.error i { color: var(--error); } .notification.warning { border-left-color: var(--warning); } .notification.warning i { color: var(--warning); } .notification-icon { font-size: 1.2rem; margin-right: var(--space-md); } .notification-message { flex: 1; font-size: 0.9rem; } @keyframes slide-in { to { transform: translateX(0); opacity: 1; } } /* Responsive styles */ @media screen and (max-width: 1024px) { .analyzer-dashboard { grid-template-columns: 1fr; } .dashboard-sidebar { position: static; margin-bottom: var(--space-xl); } .sidebar-section { margin-bottom: var(--space-md); } .export-options { grid-template-columns: 1fr; } .genetic-testing-form { grid-template-columns: 1fr; } .ancestry-chart { flex-direction: column; align-items: center; } .ancestry-categories { margin-left: 0; margin-top: var(--space-lg); width: 100%; } } @media screen and (max-width: 768px) { .options-grid { grid-template-columns: 1fr; } .analyzer-tabs { flex-direction: column; } .testing-options { grid-template-columns: 1fr; } .risk-cards { grid-template-columns: 1fr; } .traits-grid { grid-template-columns: 1fr; } } @media screen and (max-width: 480px) { .results-header { flex-direction: column; align-items: flex-start; gap: var(--space-md); } .results-actions { width: 100%; flex-direction: column; align-items: flex-start; gap: var(--space-sm); } .results-grid { grid-template-columns: 1fr; } .notification { min-width: auto; max-width: 90%; } .batch-result-stats { flex-direction: column; gap: var(--space-xs); } } -------------------------------------------------- File: ./web/analyzer/analyzer.js -------------------------------------------------- /** * DNAnalyzer - Analyzer Page JavaScript * Handles all DNA analysis functionality, file handling, and result visualization */ document.addEventListener('DOMContentLoaded', function() { // Initialize UI components initializeUI(); // Check API status checkAPIStatus(); }); /** * Initialize all UI components and event listeners */ function initializeUI() { // Initialize file upload functionality initFileUpload(); // Initialize tabs initTabs(); // Initialize modals initModals(); // Initialize analyze button document.getElementById('analyzeBtn').addEventListener('click', handleAnalysis); // Initialize quick action buttons document.getElementById('importSampleBtn').addEventListener('click', importSample); document.getElementById('clearOptionsBtn').addEventListener('click', clearOptions); document.getElementById('helpBtn').addEventListener('click', showHelpModal); } /** * Initialize file upload area with drag and drop functionality */ function initFileUpload() { const dropZone = document.getElementById('fileDropZone'); const fileInput = document.getElementById('fileInput'); // Handle click on drop zone dropZone.addEventListener('click', function() { fileInput.click(); }); // Handle file selection via input fileInput.addEventListener('change', function(e) { if (e.target.files.length > 0) { handleFileSelection(e.target.files[0]); } }); // Handle drag and drop events dropZone.addEventListener('dragover', function(e) { e.preventDefault(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', function() { dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', function(e) { e.preventDefault(); dropZone.classList.remove('drag-over'); if (e.dataTransfer.files.length > 0) { handleFileSelection(e.dataTransfer.files[0]); } }); } /** * Handle the selected file and update UI * @param {File} file - The selected file */ function handleFileSelection(file) { // Check if file type is supported const validExtensions = ['.fa', '.fasta', '.fastq', '.txt']; const fileName = file.name.toLowerCase(); const isValid = validExtensions.some(ext => fileName.endsWith(ext)); if (!isValid) { showNotification('Unsupported file format. Please upload a FASTA, FASTQ, or TXT file.', 'error'); return; } // Store file in global state for later use window.selectedFile = file; // Update UI with file info const dropZone = document.getElementById('fileDropZone'); dropZone.innerHTML = `
${file.name}
${formatFileSize(file.size)}
`; // Add event listener to remove button document.getElementById('removeFileBtn').addEventListener('click', function(e) { e.stopPropagation(); resetFileUpload(); }); // Enable analyze button document.getElementById('analyzeBtn').disabled = false; showNotification(`File "${file.name}" ready for analysis.`, 'success'); } /** * Reset the file upload area to its initial state */ function resetFileUpload() { const dropZone = document.getElementById('fileDropZone'); const fileInput = document.getElementById('fileInput'); // Clear the file input fileInput.value = ''; // Reset global state window.selectedFile = null; // Reset UI dropZone.innerHTML = `

Upload DNA Sequence

Drag and drop your sequence file here or click to browse

Supported formats: .fa, .fasta, .fastq, .txt `; // Disable analyze button document.getElementById('analyzeBtn').disabled = true; } /** * Initialize tab functionality */ function initTabs() { const tabButtons = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabButtons.forEach(button => { button.addEventListener('click', function() { const tabId = this.getAttribute('data-tab'); // Remove active class from all tabs tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // Add active class to current tab this.classList.add('active'); document.getElementById(tabId).classList.add('active'); }); }); } /** * Initialize modal functionality */ function initModals() { // Export modal const exportModal = document.getElementById('exportModal'); const closeExportModal = document.getElementById('closeExportModal'); if (exportModal && closeExportModal) { document.getElementById('exportBtn').addEventListener('click', function() { exportModal.classList.add('active'); }); closeExportModal.addEventListener('click', function() { exportModal.classList.remove('active'); }); // Close modal when clicking outside exportModal.addEventListener('click', function(e) { if (e.target === exportModal) { exportModal.classList.remove('active'); } }); // Handle export format selection document.querySelectorAll('.export-option').forEach(option => { option.addEventListener('click', function() { const format = this.getAttribute('data-format'); downloadResults(format); exportModal.classList.remove('active'); }); }); } // Help modal const helpModal = document.getElementById('helpModal'); const closeHelpModal = document.getElementById('closeHelpModal'); if (helpModal && closeHelpModal) { closeHelpModal.addEventListener('click', function() { helpModal.classList.remove('active'); }); // Close modal when clicking outside helpModal.addEventListener('click', function(e) { if (e.target === helpModal) { helpModal.classList.remove('active'); } }); } } /** * Show the help modal */ function showHelpModal() { const helpModal = document.getElementById('helpModal'); if (helpModal) { helpModal.classList.add('active'); } } /** * Import a sample DNA file for demonstration */ function importSample() { // Create a mock file from a predefined DNA sequence const sampleDNA = '>Sample DNA Sequence\nATGGCCTAGCTAGCTGATCGATCGATCGATCGATCGATCGATCGATCGATCGACTGATCTAGCATCGATCGATCGAATCGCTAGCTAGCATCGATCGATCGATCGATCGACTAGCTAGCTATCAGCTAGCTAGCTAGCTAGCTCGATCGATCGATCGATCGATCGACTAGCTAGCAGACTAGCTAGCATCATCGACTAGCTGATGATGGATTAGCATCGATCGATAGCTACGAT'; const blob = new Blob([sampleDNA], { type: 'text/plain' }); const file = new File([blob], 'sample_dna.fa', { type: 'text/plain' }); // Process the sample file handleFileSelection(file); } /** * Reset all analysis options to default */ function clearOptions() { document.querySelectorAll('input[name="analysis-option"]').forEach(checkbox => { // Set default checkboxes (first 5) to checked, others to unchecked const isDefault = ['sequence-length', 'gc-content', 'base-composition', 'start-codons', 'stop-codons'].includes(checkbox.value); checkbox.checked = isDefault; }); showNotification('Analysis options reset to default.', 'success'); } /** * Check API connectivity status */ function checkAPIStatus() { const apiStatus = document.getElementById('apiStatus'); if (!apiStatus) return; // Check if API client is available if (typeof DNAnalyzerAPI === 'undefined') { apiStatus.innerHTML = `
API not available
`; return; } // Try to connect to the API DNAnalyzerAPI.checkStatus() .then(status => { console.log('API Status:', status); apiStatus.innerHTML = `
API Connected v${status.version || '1.0'}
`; }) .catch(error => { console.error('API Error:', error); apiStatus.innerHTML = `
API Offline - Using Fallback Mode
`; }); } /** * Handle the DNA analysis process */ function handleAnalysis() { if (!window.selectedFile) { showNotification('Please upload a DNA sequence file first.', 'error'); return; } // Get selected analysis options const options = getSelectedOptions(); // Show loading state const analyzeBtn = document.getElementById('analyzeBtn'); analyzeBtn.disabled = true; analyzeBtn.innerHTML = ' Analyzing...'; // Show results section with loading state const resultsSection = document.getElementById('resultsSection'); resultsSection.style.display = 'block'; const resultsContent = document.getElementById('resultsContent'); resultsContent.innerHTML = `

Analyzing DNA sequence...

`; // Update file info in results header document.getElementById('resultsFileInfo').innerHTML = ` ${window.selectedFile.name} `; // Scroll to results resultsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Try using API if available, otherwise use fallback if (typeof DNAnalyzerAPI !== 'undefined') { try { performAPIAnalysis(window.selectedFile, options); } catch (error) { console.error('API Error:', error); performFallbackAnalysis(window.selectedFile, options); } } else { performFallbackAnalysis(window.selectedFile, options); } } /** * Perform analysis using the API * @param {File} file - The file to analyze * @param {Object} options - Analysis options */ function performAPIAnalysis(file, options) { // First try to analyze via API DNAnalyzerAPI.analyzeDNA(file, options) .then(results => { displayResults(results); // Add to history addToHistory(file.name); // Reset button const analyzeBtn = document.getElementById('analyzeBtn'); analyzeBtn.disabled = false; analyzeBtn.innerHTML = ' Start Analysis'; showNotification('Analysis completed successfully!', 'success'); }) .catch(error => { console.error('API Analysis Error:', error); // Fallback to local analysis performFallbackAnalysis(file, options); }); } /** * Perform analysis using local fallback method * @param {File} file - The file to analyze * @param {Object} options - Analysis options */ function performFallbackAnalysis(file, options) { // Read the file content const reader = new FileReader(); reader.onload = function(e) { const sequence = parseFastaSequence(e.target.result); // Perform basic sequence analysis const results = analyzeSequence(sequence, options); // Display results displayResults(results); // Add to history addToHistory(file.name); // Reset button const analyzeBtn = document.getElementById('analyzeBtn'); analyzeBtn.disabled = false; analyzeBtn.innerHTML = ' Start Analysis'; showNotification('Analysis completed using fallback mode.', 'success'); }; reader.onerror = function() { showNotification('Failed to read file. Please try again.', 'error'); // Reset button const analyzeBtn = document.getElementById('analyzeBtn'); analyzeBtn.disabled = false; analyzeBtn.innerHTML = ' Start Analysis'; }; reader.readAsText(file); } /** * Parse FASTA format sequence * @param {string} fastaData - Raw FASTA file content * @returns {string} - Cleaned DNA sequence */ function parseFastaSequence(fastaData) { // Split by lines const lines = fastaData.split(/\r?\n/); // Skip header lines (starting with '>') and join the rest let sequence = ''; let foundHeader = false; for (let line of lines) { line = line.trim(); if (line.startsWith('>')) { foundHeader = true; continue; } if (line.length > 0) { sequence += line; } } // If no header was found, assume the entire content is the sequence if (!foundHeader && sequence === '') { sequence = fastaData.replace(/\s+/g, ''); } return sequence.toUpperCase(); } /** * Analyze DNA sequence * @param {string} sequence - DNA sequence * @param {Object} options - Analysis options * @returns {Object} - Analysis results */ function analyzeSequence(sequence, options) { const results = { sequence: { length: sequence.length, sample: sequence.substring(0, 100) + (sequence.length > 100 ? '...' : '') }, basePairs: analyzeBasePairs(sequence), options: options }; // Codon analysis if selected if (options.includes('start-codons') || options.includes('stop-codons')) { results.codons = analyzeCodonFrequency(sequence); } // Reading frames if selected if (options.includes('reading-frames')) { results.readingFrames = analyzeReadingFrames(sequence); } // Protein prediction if selected if (options.includes('protein-prediction')) { results.proteins = findPotentialProteins(sequence); } return results; } /** * Analyze base pair composition * @param {string} sequence - DNA sequence * @returns {Object} - Base pair analysis results */ function analyzeBasePairs(sequence) { const counts = { A: 0, T: 0, G: 0, C: 0, other: 0 }; // Count occurrences of each base for (let i = 0; i < sequence.length; i++) { const base = sequence[i]; if (counts.hasOwnProperty(base)) { counts[base]++; } else { counts.other++; } } // Calculate percentages const total = sequence.length; const percentages = {}; for (const base in counts) { percentages[base] = parseFloat(((counts[base] / total) * 100).toFixed(1)); } // Calculate GC content const gcContent = parseFloat(((counts.G + counts.C) / total * 100).toFixed(1)); return { counts, percentages, gcContent }; } /** * Analyze codon frequency * @param {string} sequence - DNA sequence * @returns {Object} - Codon analysis results */ function analyzeCodonFrequency(sequence) { const startCodon = 'ATG'; const stopCodons = ['TAA', 'TAG', 'TGA']; let startCount = 0; let stopCount = 0; // Analyze each possible codon position for (let i = 0; i < sequence.length - 2; i++) { const codon = sequence.substring(i, i + 3); if (codon === startCodon) { startCount++; } if (stopCodons.includes(codon)) { stopCount++; } } return { startCodons: startCount, stopCodons: stopCount }; } /** * Analyze reading frames * @param {string} sequence - DNA sequence * @returns {Object} - Reading frames analysis */ function analyzeReadingFrames(sequence) { const frames = []; const startCodon = 'ATG'; const stopCodons = ['TAA', 'TAG', 'TGA']; // Analyze each of the three forward reading frames for (let offset = 0; offset < 3; offset++) { const frame = { frame: offset + 1, direction: 'forward', genes: [] }; let inGene = false; let geneStart = 0; for (let i = offset; i < sequence.length - 2; i += 3) { const codon = sequence.substring(i, i + 3); if (codon === startCodon && !inGene) { inGene = true; geneStart = i; } else if (stopCodons.includes(codon) && inGene) { inGene = false; const gene = { start: geneStart, end: i + 2, length: i + 3 - geneStart }; frame.genes.push(gene); } } frames.push(frame); } // Get the reverse complement sequence const reverseComplement = getReverseComplement(sequence); // Analyze each of the three reverse reading frames for (let offset = 0; offset < 3; offset++) { const frame = { frame: offset + 4, direction: 'reverse', genes: [] }; let inGene = false; let geneStart = 0; for (let i = offset; i < reverseComplement.length - 2; i += 3) { const codon = reverseComplement.substring(i, i + 3); if (codon === startCodon && !inGene) { inGene = true; geneStart = i; } else if (stopCodons.includes(codon) && inGene) { inGene = false; const gene = { start: i + 2, end: geneStart, length: i + 3 - geneStart }; frame.genes.push(gene); } } frames.push(frame); } return { frames: frames, totalGenes: frames.reduce((total, frame) => total + frame.genes.length, 0) }; } /** * Get reverse complement of DNA sequence * @param {string} sequence - DNA sequence * @returns {string} - Reverse complement */ function getReverseComplement(sequence) { const complementMap = { 'A': 'T', 'T': 'A', 'G': 'C', 'C': 'G', 'N': 'N' }; let reverseComplement = ''; for (let i = sequence.length - 1; i >= 0; i--) { const base = sequence[i]; reverseComplement += complementMap[base] || base; } return reverseComplement; } /** * Find potential proteins in DNA sequence * @param {string} sequence - DNA sequence * @returns {Array} - List of potential proteins */ function findPotentialProteins(sequence) { const startCodon = 'ATG'; const stopCodons = ['TAA', 'TAG', 'TGA']; const proteins = []; // Genetic code for translation const geneticCode = { 'TTT': 'F', 'TTC': 'F', 'TTA': 'L', 'TTG': 'L', 'CTT': 'L', 'CTC': 'L', 'CTA': 'L', 'CTG': 'L', 'ATT': 'I', 'ATC': 'I', 'ATA': 'I', 'ATG': 'M', 'GTT': 'V', 'GTC': 'V', 'GTA': 'V', 'GTG': 'V', 'TCT': 'S', 'TCC': 'S', 'TCA': 'S', 'TCG': 'S', 'CCT': 'P', 'CCC': 'P', 'CCA': 'P', 'CCG': 'P', 'ACT': 'T', 'ACC': 'T', 'ACA': 'T', 'ACG': 'T', 'GCT': 'A', 'GCC': 'A', 'GCA': 'A', 'GCG': 'A', 'TAT': 'Y', 'TAC': 'Y', 'TAA': '*', 'TAG': '*', 'CAT': 'H', 'CAC': 'H', 'CAA': 'Q', 'CAG': 'Q', 'AAT': 'N', 'AAC': 'N', 'AAA': 'K', 'AAG': 'K', 'GAT': 'D', 'GAC': 'D', 'GAA': 'E', 'GAG': 'E', 'TGT': 'C', 'TGC': 'C', 'TGA': '*', 'TGG': 'W', 'CGT': 'R', 'CGC': 'R', 'CGA': 'R', 'CGG': 'R', 'AGT': 'S', 'AGC': 'S', 'AGA': 'R', 'AGG': 'R', 'GGT': 'G', 'GGC': 'G', 'GGA': 'G', 'GGG': 'G' }; // Search for proteins in all three reading frames for (let offset = 0; offset < 3; offset++) { let i = offset; while (i < sequence.length - 2) { const codon = sequence.substring(i, i + 3); // If we find a start codon, begin translating if (codon === startCodon) { let proteinStart = i; let protein = 'M'; // Start with methionine let j = i + 3; // Continue until stop codon or end of sequence while (j < sequence.length - 2) { const nextCodon = sequence.substring(j, j + 3); // Check if it's a stop codon if (stopCodons.includes(nextCodon)) { // We found a complete protein if (protein.length >= 10) { // Only include proteins of significant length proteins.push({ start: proteinStart, end: j + 2, length: protein.length, sequence: protein }); } // Move past this protein i = j + 3; break; } // Add amino acid to protein const aa = geneticCode[nextCodon] || 'X'; // X for unknown protein += aa; j += 3; } // If we reached the end without finding a stop codon if (j >= sequence.length - 2) { i = j; } } else { i += 3; } } } // Sort by length (longest first) proteins.sort((a, b) => b.length - a.length); // Limit to top 10 return proteins.slice(0, 10); } /** * Display analysis results * @param {Object} results - Analysis results */ function displayResults(results) { const resultsContent = document.getElementById('resultsContent'); const options = results.options; // Basic results grid let html = '
'; // Sequence length if (options.includes('sequence-length')) { html += `
${results.sequence.length.toLocaleString()}
Sequence Length
`; } // GC content if (options.includes('gc-content')) { html += `
${results.basePairs.gcContent}%
GC Content
`; } // Start and stop codons if (options.includes('start-codons') && results.codons) { html += `
${results.codons.startCodons}
Start Codons
`; } if (options.includes('stop-codons') && results.codons) { html += `
${results.codons.stopCodons}
Stop Codons
`; } // Reading frames if (options.includes('reading-frames') && results.readingFrames) { html += `
${results.readingFrames.totalGenes}
Potential Genes
`; } // Protein prediction if (options.includes('protein-prediction') && results.proteins) { html += `
${results.proteins.length}
Proteins Found
`; // Add longest protein info if available if (results.proteins.length > 0) { html += `
${results.proteins[0].length}
Longest Protein (aa)
`; } } html += '
'; // End of results grid // Base composition chart if (options.includes('base-composition')) { const basePercentages = results.basePairs.percentages; html += `

Base Composition

A
T
G
C
${basePercentages.other > 0 ? `
N
` : ''}
Adenine (A): ${basePercentages.A}%
Thymine (T): ${basePercentages.T}%
Guanine (G): ${basePercentages.G}%
Cytosine (C): ${basePercentages.C}%
${basePercentages.other > 0 ? `
Other (N): ${basePercentages.other}%
` : ''}
`; } // Reading frames table if (options.includes('reading-frames') && results.readingFrames && results.readingFrames.frames) { html += `

Reading Frames

`; results.readingFrames.frames.forEach(frame => { html += ` `; }); html += `
Frame Direction Genes Found
${frame.frame} ${frame.direction} ${frame.genes.length}
`; } // Proteins table if (options.includes('protein-prediction') && results.proteins && results.proteins.length > 0) { html += `

Predicted Proteins

`; results.proteins.forEach((protein, index) => { const proteinSeq = protein.sequence || protein; const displaySeq = proteinSeq.substring(0, 30) + (proteinSeq.length > 30 ? '...' : ''); html += ` `; }); html += `
# Length Sequence (First 30 aa)
${index + 1} ${protein.length || proteinSeq.length} ${displaySeq}
`; } // Download options html += `

Download Results

`; // Update the results content resultsContent.innerHTML = html; // Add event listeners to download buttons document.querySelectorAll('.download-format').forEach(btn => { btn.addEventListener('click', function() { const format = this.getAttribute('data-format'); downloadResults(format, results); }); }); // Store results for later use window.analysisResults = results; } /** * Download analysis results * @param {string} format - File format (json, csv, txt) * @param {Object} results - Analysis results */ function downloadResults(format, results = window.analysisResults) { if (!results) { showNotification('No analysis results to download.', 'error'); return; } let content = ''; let fileName = `dna_analysis_${new Date().toISOString().slice(0, 10)}`; let mimeType = 'text/plain'; if (format === 'json') { content = JSON.stringify(results, null, 2); fileName += '.json'; mimeType = 'application/json'; } else if (format === 'csv') { // Basic info content = 'Property,Value\n'; content += `Sequence Length,${results.sequence.length}\n`; content += `GC Content,${results.basePairs.gcContent}%\n\n`; // Base pairs content += 'Base,Count,Percentage\n'; for (const base in results.basePairs.counts) { content += `${base},${results.basePairs.counts[base]},${results.basePairs.percentages[base]}%\n`; } content += '\n'; // Add codon info if available if (results.codons) { content += 'Codon Type,Count\n'; content += `Start Codons,${results.codons.startCodons}\n`; content += `Stop Codons,${results.codons.stopCodons}\n\n`; } // Add reading frames if available if (results.readingFrames && results.readingFrames.frames) { content += 'Frame,Direction,Genes Found\n'; results.readingFrames.frames.forEach(frame => { content += `${frame.frame},${frame.direction},${frame.genes.length}\n`; }); content += '\n'; } // Add proteins if available if (results.proteins && results.proteins.length > 0) { content += 'Protein,Length,Sequence\n'; results.proteins.forEach((protein, index) => { const proteinSeq = protein.sequence || protein; content += `${index + 1},${protein.length || proteinSeq.length},"${proteinSeq}"\n`; }); } fileName += '.csv'; mimeType = 'text/csv'; } else { // txt format content = 'DNA Sequence Analysis Results\n'; content += '============================\n\n'; // Basic info content += `Sequence Length: ${results.sequence.length}\n`; content += `GC Content: ${results.basePairs.gcContent}%\n\n`; // Base composition content += 'Base Composition:\n'; for (const base in results.basePairs.counts) { content += `${base}: ${results.basePairs.counts[base]} (${results.basePairs.percentages[base]}%)\n`; } content += '\n'; // Add codon info if available if (results.codons) { content += 'Codon Analysis:\n'; content += `Start Codons (ATG): ${results.codons.startCodons}\n`; content += `Stop Codons (TAA, TAG, TGA): ${results.codons.stopCodons}\n\n`; } // Add reading frames if available if (results.readingFrames && results.readingFrames.frames) { content += 'Reading Frames:\n'; results.readingFrames.frames.forEach(frame => { content += `Frame ${frame.frame} (${frame.direction}): ${frame.genes.length} genes found\n`; }); content += '\n'; } // Add proteins if available if (results.proteins && results.proteins.length > 0) { content += 'Predicted Proteins:\n'; results.proteins.forEach((protein, index) => { const proteinSeq = protein.sequence || protein; content += `Protein ${index + 1} (${protein.length || proteinSeq.length} aa): ${proteinSeq}\n`; }); } content += '\nAnalysis Date: ' + new Date().toLocaleString(); fileName += '.txt'; } // Create download link const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; a.click(); // Cleanup setTimeout(() => URL.revokeObjectURL(url), 100); showNotification(`Results saved as ${fileName}`, 'success'); } /** * Add an analysis to the history * @param {string} fileName - Name of the analyzed file */ function addToHistory(fileName) { const historyList = document.getElementById('historyList'); // Remove empty state if present const emptyState = historyList.querySelector('.history-empty'); if (emptyState) { historyList.removeChild(emptyState); } // Create new history item const historyItem = document.createElement('div'); historyItem.className = 'history-item'; const now = new Date(); const timeString = now.toLocaleTimeString(); historyItem.innerHTML = `
${fileName}
${timeString}
`; // Add to history (prepend) historyList.insertBefore(historyItem, historyList.firstChild); // Limit to 5 items const items = historyList.querySelectorAll('.history-item'); if (items.length > 5) { historyList.removeChild(items[items.length - 1]); } } /** * Get selected analysis options * @returns {Array} - List of selected option values */ function getSelectedOptions() { const checkboxes = document.querySelectorAll('input[name="analysis-option"]:checked'); const options = []; checkboxes.forEach(checkbox => { options.push(checkbox.value); }); return options; } /** * Format file size in human-readable format * @param {number} bytes - File size in bytes * @returns {string} - Formatted file size */ function formatFileSize(bytes) { if (bytes < 1024) { return bytes + ' bytes'; } else if (bytes < 1024 * 1024) { return (bytes / 1024).toFixed(1) + ' KB'; } else { return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } } /** * Show a notification * @param {string} message - Notification message * @param {string} type - Notification type (success, error, warning, info) * @param {number} duration - Duration in milliseconds */ function showNotification(message, type = 'info', duration = 5000) { const container = document.getElementById('notificationContainer'); // Create notification element const notification = document.createElement('div'); notification.className = `notification ${type}`; // Set icon based on type let icon = 'info-circle'; if (type === 'success') icon = 'check-circle'; if (type === 'error') icon = 'times-circle'; if (type === 'warning') icon = 'exclamation-triangle'; notification.innerHTML = `
${message}
`; // Add to container container.appendChild(notification); // Remove after duration setTimeout(() => { notification.style.opacity = '0'; notification.style.transform = 'translateX(100%)'; // Remove from DOM after animation setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, duration); } -------------------------------------------------- File: ./web/analyzer/genetic-testing.js -------------------------------------------------- /** * DNAnalyzer - Genetic Testing Module * Handles genetic testing functionality including file handling, analysis, and visualization */ // Initialize genetic testing functionality when DOM is loaded document.addEventListener('DOMContentLoaded', function() { if (document.getElementById('genetic-testing')) { initGeneticTesting(); } }); /** * Initialize genetic testing functionality */ function initGeneticTesting() { // Initialize file upload for genetic data initGeneticFileUpload(); // Initialize test options selection initTestOptions(); // Initialize consent checkbox initConsentCheckbox(); // Initialize analyze button const geneticAnalyzeBtn = document.getElementById('geneticAnalyzeBtn'); if (geneticAnalyzeBtn) { geneticAnalyzeBtn.addEventListener('click', handleGeneticAnalysis); } // Add sample data button addSampleDataButton(); } /** * Initialize genetic file upload with drag and drop functionality */ function initGeneticFileUpload() { const dropZone = document.getElementById('geneticFileDropZone'); const fileInput = document.getElementById('geneticFileInput'); if (!dropZone || !fileInput) return; // Handle click on drop zone dropZone.addEventListener('click', function() { fileInput.click(); }); // Handle file selection via input fileInput.addEventListener('change', function(e) { if (e.target.files.length > 0) { handleGeneticFileSelection(e.target.files[0]); } }); // Handle drag and drop events dropZone.addEventListener('dragover', function(e) { e.preventDefault(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', function() { dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', function(e) { e.preventDefault(); dropZone.classList.remove('drag-over'); if (e.dataTransfer.files.length > 0) { handleGeneticFileSelection(e.dataTransfer.files[0]); } }); } /** * Handle the selected genetic data file * @param {File} file - The selected file */ function handleGeneticFileSelection(file) { const dropZone = document.getElementById('geneticFileDropZone'); const fileInput = document.getElementById('geneticFileInput'); const geneticAnalyzeBtn = document.getElementById('geneticAnalyzeBtn'); if (!dropZone || !fileInput || !geneticAnalyzeBtn) return; // Check file type const validExtensions = ['.txt', '.csv', '.vcf']; const fileExt = file.name.substring(file.name.lastIndexOf('.')).toLowerCase(); if (!validExtensions.includes(fileExt)) { showNotification('Invalid file format. Please upload a .txt, .csv, or .vcf file.', 'error'); return; } // Update UI to show selected file dropZone.innerHTML = `

${file.name}

${formatFileSize(file.size)}
`; // Add remove button functionality const removeBtn = dropZone.querySelector('.file-remove'); if (removeBtn) { removeBtn.addEventListener('click', function(e) { e.stopPropagation(); resetGeneticFileUpload(); }); } // Store file in global variable for later use window.selectedGeneticFile = file; // Enable analyze button if consent is checked const consentCheckbox = document.getElementById('consentCheckbox'); if (consentCheckbox && consentCheckbox.checked) { geneticAnalyzeBtn.disabled = false; } // Show notification showNotification('Genetic data file selected. Complete the form and select testing options.', 'success'); } /** * Reset the genetic file upload area to its initial state */ function resetGeneticFileUpload() { const dropZone = document.getElementById('geneticFileDropZone'); const fileInput = document.getElementById('geneticFileInput'); const geneticAnalyzeBtn = document.getElementById('geneticAnalyzeBtn'); if (!dropZone || !fileInput || !geneticAnalyzeBtn) return; // Reset input value fileInput.value = ''; // Reset drop zone content dropZone.innerHTML = `

Upload Genetic Data

Drag and drop your genetic data file here or click to browse

Supported formats: .txt, .csv, .vcf (23andMe, AncestryDNA, etc.) `; // Disable analyze button geneticAnalyzeBtn.disabled = true; // Clear stored file window.selectedGeneticFile = null; } /** * Initialize test options selection */ function initTestOptions() { const testOptions = document.querySelectorAll('.test-option'); testOptions.forEach(option => { option.addEventListener('click', function() { this.classList.toggle('selected'); }); }); } /** * Initialize consent checkbox */ function initConsentCheckbox() { const consentCheckbox = document.getElementById('consentCheckbox'); const geneticAnalyzeBtn = document.getElementById('geneticAnalyzeBtn'); if (!consentCheckbox || !geneticAnalyzeBtn) return; consentCheckbox.addEventListener('change', function() { // Enable analyze button only if both file is selected and consent is checked geneticAnalyzeBtn.disabled = !(this.checked && window.selectedGeneticFile); }); } /** * Add sample data button to the genetic testing tab */ function addSampleDataButton() { const uploadFormats = document.querySelector('#geneticFileDropZone .upload-formats'); if (!uploadFormats) return; const sampleBtn = document.createElement('button'); sampleBtn.className = 'btn btn-secondary btn-sm sample-data-btn'; sampleBtn.innerHTML = ' Load Sample Data'; sampleBtn.addEventListener('click', function(e) { e.stopPropagation(); loadSampleGeneticData(); }); uploadFormats.insertAdjacentElement('afterend', sampleBtn); } /** * Load sample genetic data for demonstration */ function loadSampleGeneticData() { // Create a sample file const sampleData = createSampleGeneticData(); const blob = new Blob([sampleData], { type: 'text/plain' }); const file = new File([blob], 'sample-genetic-data.txt', { type: 'text/plain' }); // Process the sample file handleGeneticFileSelection(file); // Pre-fill form with sample data document.getElementById('gender').value = 'male'; document.getElementById('age').value = '35'; document.getElementById('ethnicity').value = 'european'; // Select all test options document.querySelectorAll('.test-option').forEach(option => { option.classList.add('selected'); }); // Check consent checkbox const consentCheckbox = document.getElementById('consentCheckbox'); if (consentCheckbox) { consentCheckbox.checked = true; // Trigger change event const event = new Event('change'); consentCheckbox.dispatchEvent(event); } // Show notification showNotification('Sample genetic data loaded successfully!', 'success'); } /** * Create sample genetic data in 23andMe format * @returns {string} - Sample genetic data */ function createSampleGeneticData() { return `# This data is for demonstration purposes only # Sample genetic data in 23andMe format # This file contains a small subset of SNPs for educational purposes # rsid\tchromosome\tposition\tgenotype rs4477212\t1\t82154\tAA rs3094315\t1\t752566\tAG rs12562034\t1\t768448\tGG rs3934834\t1\t1005806\tCC rs3737728\t1\t1011278\tAG rs11260588\t1\t1021454\tGG rs2905036\t1\t1021915\tCC rs2973725\t1\t1025314\tAG rs2980300\t1\t1041900\tTT rs4475691\t1\t1051029\tCC rs11497407\t1\t1061166\tAA rs11260603\t1\t1070488\tGG rs2341354\t1\t1088657\tAG rs2341355\t1\t1088761\tTT rs1320571\t1\t1089876\tAG rs11260614\t1\t1093709\tAA rs2978398\t1\t1099810\tGG rs2820323\t1\t1105366\tAG rs2887286\t1\t1110240\tAA rs1815606\t1\t1110294\tCC rs2978381\t1\t1114147\tGG rs7526076\t1\t1117281\tAA rs2887286\t1\t1110240\tAA rs1815606\t1\t1110294\tCC`; } /** * Handle genetic analysis process */ function handleGeneticAnalysis() { // Get selected file const file = window.selectedGeneticFile; if (!file) { showNotification('Please upload a genetic data file first.', 'warning'); return; } // Get selected tests const selectedTests = []; document.querySelectorAll('.test-option.selected').forEach(option => { selectedTests.push(option.dataset.test); }); if (selectedTests.length === 0) { showNotification('Please select at least one test to perform.', 'warning'); return; } // Get form data const formData = { gender: document.getElementById('gender').value, age: document.getElementById('age').value, ethnicity: document.getElementById('ethnicity').value }; // Start analysis showNotification('Starting genetic analysis...', 'info'); // Show loading state const analyzeBtn = document.getElementById('geneticAnalyzeBtn'); if (analyzeBtn) { analyzeBtn.disabled = true; analyzeBtn.innerHTML = ' Analyzing...'; } // Simulate processing delay setTimeout(() => { // Perform analysis const results = performGeneticAnalysis(file, selectedTests, formData); // Display results displayGeneticResults(results); // Reset button if (analyzeBtn) { analyzeBtn.disabled = false; analyzeBtn.innerHTML = ' Run Genetic Tests'; } }, 2000); } /** * Perform genetic analysis on the provided file * @param {File} file - Genetic data file * @param {Array} selectedTests - Array of selected test types * @param {Object} formData - Personal information form data * @returns {Object} - Analysis results */ function performGeneticAnalysis(file, selectedTests, formData) { // This is a simplified demonstration that returns pre-defined results // In a real implementation, this would parse the file and analyze the data // Sample results for demonstration return { summary: { totalSnps: 24, analyzedSnps: 24, coverage: "0.001%", quality: "High" }, health: { risks: [ { condition: "Type 2 Diabetes", risk: "Slightly Elevated", snps: ["rs4477212", "rs3094315"], confidence: "Medium" }, { condition: "Coronary Heart Disease", risk: "Average", snps: ["rs12562034", "rs3934834"], confidence: "High" }, { condition: "Age-related Macular Degeneration", risk: "Lower", snps: ["rs3737728"], confidence: "Medium" }, { condition: "Alzheimer's Disease", risk: "Average", snps: ["rs11260588", "rs2905036"], confidence: "Low" } ] }, traits: { physical: [ { trait: "Eye Color", prediction: "Brown", snps: ["rs2973725", "rs2980300"], confidence: "High" }, { trait: "Hair Type", prediction: "Straight", snps: ["rs4475691"], confidence: "Medium" }, { trait: "Earwax Type", prediction: "Wet", snps: ["rs11497407"], confidence: "High" }, { trait: "Lactose Intolerance", prediction: "Likely Tolerant", snps: ["rs11260603"], confidence: "Medium" } ], behavioral: [ { trait: "Caffeine Metabolism", prediction: "Fast Metabolizer", snps: ["rs2341354"], confidence: "Medium" }, { trait: "Alcohol Flush Reaction", prediction: "No Reaction", snps: ["rs2341355"], confidence: "High" } ] }, ancestry: { composition: [ { population: "Northern European", percentage: 68 }, { population: "Southern European", percentage: 12 }, { population: "Eastern European", percentage: 8 }, { population: "Northwest Asian", percentage: 6 }, { population: "Other", percentage: 6 } ], haplogroups: { maternal: "H1c", paternal: "R1b1b2a1a" } }, carrier: { conditions: [ { condition: "Cystic Fibrosis", status: "Not a Carrier", snps: ["rs1320571"], confidence: "High" }, { condition: "Sickle Cell Anemia", status: "Not a Carrier", snps: ["rs11260614"], confidence: "High" }, { condition: "Tay-Sachs Disease", status: "Not a Carrier", snps: ["rs2978398"], confidence: "Medium" }, { condition: "Phenylketonuria", status: "Not a Carrier", snps: ["rs2820323"], confidence: "High" } ] } }; } /** * Display genetic analysis results * @param {Object} results - Analysis results */ function displayGeneticResults(results) { // Get results section and make it visible const resultsSection = document.getElementById('resultsSection'); if (!resultsSection) return; resultsSection.style.display = 'block'; resultsSection.scrollIntoView({ behavior: 'smooth' }); // Update summary section const summaryElement = document.querySelector('.results-summary'); if (summaryElement) { summaryElement.innerHTML = `

Genetic Analysis Results

${results.summary.totalSnps} SNPs Analyzed
${results.summary.coverage} Genome Coverage
${results.summary.quality} Data Quality
`; } // Update content section with tabs for each test type const contentElement = document.querySelector('.results-content'); if (!contentElement) return; // Create tabs let tabsHtml = '
'; if (results.health) { tabsHtml += ``; } if (results.traits) { tabsHtml += ``; } if (results.ancestry) { tabsHtml += ``; } if (results.carrier) { tabsHtml += ``; } tabsHtml += '
'; // Create tab content let contentHtml = tabsHtml + '
'; // Health risks tab if (results.health) { contentHtml += `

Health Risk Assessment

Note: These results are for educational purposes only and should not be used for medical decisions.

${results.health.risks.map(risk => `

${risk.condition}

${risk.risk}
Relevant SNPs: ${risk.snps.join(', ')}
Confidence: ${risk.confidence}
`).join('')}
`; } // Traits tab if (results.traits) { contentHtml += `

Genetic Traits

Physical Traits

${results.traits.physical.map(trait => `
${trait.trait}
${trait.prediction} ${trait.confidence} confidence
SNPs: ${trait.snps.join(', ')}
`).join('')}

Behavioral Traits

${results.traits.behavioral.map(trait => `
${trait.trait}
${trait.prediction} ${trait.confidence} confidence
SNPs: ${trait.snps.join(', ')}
`).join('')}
`; } // Ancestry tab if (results.ancestry) { contentHtml += `

Ancestry Composition

${results.ancestry.composition.map(pop => `
${pop.population}
${pop.percentage}%
`).join('')}

Haplogroups

Maternal Haplogroup: ${results.ancestry.haplogroups.maternal}
Paternal Haplogroup: ${results.ancestry.haplogroups.paternal}
`; } // Carrier status tab if (results.carrier) { contentHtml += `

Carrier Status

Note: These results are for educational purposes only and should not be used for family planning decisions.

${results.carrier.conditions.map(condition => `

${condition.condition}

${condition.status}
Relevant SNPs: ${condition.snps.join(', ')}
Confidence: ${condition.confidence}
`).join('')}
`; } contentHtml += '
'; // Update content contentElement.innerHTML = contentHtml; // Initialize results tabs initResultsTabs(); // Initialize charts if ancestry results are present if (results.ancestry) { initAncestryChart(results.ancestry.composition); } // Show notification showNotification('Genetic analysis completed successfully!', 'success'); } /** * Initialize tabs in the results section */ function initResultsTabs() { const tabButtons = document.querySelectorAll('.results-tabs .tab-btn'); tabButtons.forEach(button => { button.addEventListener('click', function() { // Remove active class from all buttons and panels document.querySelectorAll('.results-tabs .tab-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelectorAll('.result-tab-panel').forEach(panel => { panel.classList.remove('active'); }); // Add active class to current button this.classList.add('active'); // Show corresponding panel const tabName = this.dataset.resultTab; const panel = document.getElementById(tabName + '-panel'); if (panel) { panel.classList.add('active'); } }); }); } /** * Initialize ancestry composition chart * @param {Array} composition - Ancestry composition data */ function initAncestryChart(composition) { const canvas = document.getElementById('ancestryChart'); if (!canvas) return; const ctx = canvas.getContext('2d'); // Extract data for chart const labels = composition.map(item => item.population); const data = composition.map(item => item.percentage); const colors = [ '#4285F4', '#EA4335', '#FBBC05', '#34A853', '#FF6D01', '#46BDC6', '#9C27B0', '#3F51B5', '#03A9F4', '#00BCD4' ]; // Create chart new Chart(ctx, { type: 'doughnut', data: { labels: labels, datasets: [{ data: data, backgroundColor: colors.slice(0, composition.length), borderColor: '#001427', borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 20, 39, 0.8)', titleFont: { family: 'Inter, sans-serif', size: 14 }, bodyFont: { family: 'Inter, sans-serif', size: 13 }, callbacks: { label: function(context) { return context.label + ': ' + context.raw + '%'; } } } }, cutout: '70%' } }); } // Utility function for formatting file size function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Utility function for showing notifications function showNotification(message, type = 'info', duration = 5000) { // Use the existing notification function from analyzer.js if available if (window.showNotification) { window.showNotification(message, type, duration); return; } // Fallback implementation const container = document.getElementById('notificationContainer'); if (!container) return; const notification = document.createElement('div'); notification.className = `notification notification-${type}`; const icon = document.createElement('i'); switch (type) { case 'success': icon.className = 'fas fa-check-circle'; break; case 'error': icon.className = 'fas fa-exclamation-circle'; break; case 'warning': icon.className = 'fas fa-exclamation-triangle'; break; default: icon.className = 'fas fa-info-circle'; } const content = document.createElement('div'); content.className = 'notification-content'; content.textContent = message; notification.appendChild(icon); notification.appendChild(content); container.appendChild(notification); // Show animation setTimeout(() => { notification.classList.add('show'); }, 10); // Hide after duration setTimeout(() => { notification.classList.remove('show'); setTimeout(() => { notification.remove(); }, 300); }, duration); } // Genetic Testing Implementation const geneticTestingModule = { // Supported file formats supportedFormats: ['.fastq', '.fasta', '.sam', '.bam', '.vcf'], // Sample DNA sequences for testing sampleSequences: { 'sample1.fastq': 'ATCGATCGATCGATCG...', 'sample2.fasta': '>Sample2\nGATTACAGATTACA...', 'sample3.vcf': '##fileformat=VCFv4.2\n#CHROM\tPOS\tID...' }, init() { this.setupEventListeners(); this.initializeUI(); }, setupEventListeners() { // File upload handling const fileInput = document.getElementById('dnaFileInput'); fileInput.addEventListener('change', (e) => this.handleFileUpload(e)); // Sample file selection const sampleSelect = document.getElementById('sampleFileSelect'); sampleSelect.addEventListener('change', (e) => this.loadSampleFile(e)); // Analysis button const analyzeBtn = document.getElementById('analyzeButton'); analyzeBtn.addEventListener('click', () => this.startAnalysis()); }, initializeUI() { // Initialize dropzone const dropZone = document.getElementById('dropZone'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, (e) => { e.preventDefault(); e.stopPropagation(); }); }); dropZone.addEventListener('drop', (e) => { const files = e.dataTransfer.files; this.handleFileUpload({ target: { files } }); }); // Add sample files to select const sampleSelect = document.getElementById('sampleFileSelect'); Object.keys(this.sampleSequences).forEach(sample => { const option = document.createElement('option'); option.value = sample; option.textContent = sample; sampleSelect.appendChild(option); }); }, async handleFileUpload(event) { const file = event.target.files[0]; if (!file) return; if (!this.validateFileFormat(file)) { this.showError(`Unsupported file format. Please use: ${this.supportedFormats.join(', ')}`); return; } try { const sequence = await this.readFile(file); this.updateUI('fileLoaded', { fileName: file.name, sequence }); } catch (error) { this.showError('Error reading file: ' + error.message); } }, loadSampleFile(event) { const sampleName = event.target.value; const sequence = this.sampleSequences[sampleName]; if (sequence) { this.updateUI('fileLoaded', { fileName: sampleName, sequence }); } }, async startAnalysis() { const sequence = this.getCurrentSequence(); if (!sequence) { this.showError('Please upload a DNA sequence file or select a sample first.'); return; } this.updateUI('analysisStarted'); try { // Perform various analyses const results = await Promise.all([ this.analyzeMutations(sequence), this.predictTraits(sequence), this.findAncestry(sequence), this.assessHealthRisks(sequence) ]); this.updateUI('analysisComplete', { results }); } catch (error) { this.showError('Analysis failed: ' + error.message); } }, // Analysis Methods async analyzeMutations(sequence) { // Implementation of mutation analysis return { type: 'mutations', findings: [ { position: 1234, mutation: 'SNP', reference: 'A', variant: 'G' }, { position: 5678, mutation: 'Deletion', reference: 'CTG', variant: '-' } ] }; }, async predictTraits(sequence) { // Implementation of trait prediction return { type: 'traits', predictions: [ { trait: 'Eye Color', prediction: 'Brown', confidence: 0.89 }, { trait: 'Height', prediction: 'Above Average', confidence: 0.75 } ] }; }, async findAncestry(sequence) { // Implementation of ancestry analysis return { type: 'ancestry', composition: [ { region: 'European', percentage: 45 }, { region: 'East Asian', percentage: 30 }, { region: 'African', percentage: 25 } ] }; }, async assessHealthRisks(sequence) { // Implementation of health risk assessment return { type: 'health', risks: [ { condition: 'Type 2 Diabetes', risk: 'Low', score: 0.2 }, { condition: 'Heart Disease', risk: 'Moderate', score: 0.4 } ] }; }, // Utility Methods validateFileFormat(file) { return this.supportedFormats.some(format => file.name.toLowerCase().endsWith(format) ); }, async readFile(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.onerror = (e) => reject(new Error('File reading failed')); reader.readAsText(file); }); }, getCurrentSequence() { const sequenceDisplay = document.getElementById('sequenceDisplay'); return sequenceDisplay?.dataset.sequence; }, showError(message) { const errorDisplay = document.getElementById('errorDisplay'); errorDisplay.textContent = message; errorDisplay.style.display = 'block'; setTimeout(() => { errorDisplay.style.display = 'none'; }, 5000); }, updateUI(state, data = {}) { const loadingSpinner = document.getElementById('loadingSpinner'); const resultsContainer = document.getElementById('resultsContainer'); const sequenceDisplay = document.getElementById('sequenceDisplay'); switch (state) { case 'fileLoaded': sequenceDisplay.textContent = `File loaded: ${data.fileName}`; sequenceDisplay.dataset.sequence = data.sequence; break; case 'analysisStarted': loadingSpinner.style.display = 'block'; resultsContainer.innerHTML = ''; break; case 'analysisComplete': loadingSpinner.style.display = 'none'; this.displayResults(data.results); break; } }, displayResults(results) { const resultsContainer = document.getElementById('resultsContainer'); resultsContainer.innerHTML = ''; results.forEach(result => { const section = document.createElement('div'); section.className = 'result-section'; switch (result.type) { case 'mutations': section.innerHTML = this.createMutationsHTML(result); break; case 'traits': section.innerHTML = this.createTraitsHTML(result); break; case 'ancestry': section.innerHTML = this.createAncestryHTML(result); break; case 'health': section.innerHTML = this.createHealthHTML(result); break; } resultsContainer.appendChild(section); }); }, createMutationsHTML(result) { return `

Genetic Mutations

${result.findings.map(mutation => `
Position: ${mutation.position} Type: ${mutation.mutation} Change: ${mutation.reference} → ${mutation.variant}
`).join('')}
`; }, createTraitsHTML(result) { return `

Predicted Traits

${result.predictions.map(trait => `
${trait.trait} ${trait.prediction}
`).join('')}
`; }, createAncestryHTML(result) { return `

Ancestry Composition

${result.composition.map(region => `
${region.region}: ${region.percentage}%
`).join('')}
`; }, createHealthHTML(result) { return `

Health Risk Assessment

${result.risks.map(risk => `
${risk.condition} ${risk.risk}
`).join('')}
`; } }; // Initialize the module when the page loads document.addEventListener('DOMContentLoaded', () => geneticTestingModule.init()); -------------------------------------------------- File: ./web/analyzer/analyzer.html -------------------------------------------------- DNA Analyzer - DNAnalyzer
Exclusive WSSEF Preview:  Transformer Architecture in Epigenomics! Explore Now

DNA Sequence Analysis

Analyze DNA sequences with advanced machine learning tools

Upload DNA Sequence

Drag and drop your sequence file here or click to browse

Supported formats: .fa, .fasta, .fastq, .txt

Analysis Options

Basic Analysis

Codon Analysis

Advanced Features

Upload DNA File or Drop Here

Supported formats: .fastq, .fasta, .sam, .bam, .vcf

Or try with a sample file:

Batch Processing Coming Soon

Process multiple DNA sequence files simultaneously for high-throughput analysis. Sign up for our newsletter to be notified when this feature launches.

-------------------------------------------------- Directory: assets ================================================== File: ./web/assets/full.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- Directory: assets/IBM Plex Mono Inter ================================================== Directory: assets/IBM Plex Mono Inter/IBM_Plex_Mono ================================================== File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Italic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Bold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Thin.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Light.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Regular.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-Medium.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/OFL.txt -------------------------------------------------- Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: https://openfontlicense.org ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- Directory: assets/IBM Plex Mono Inter/Inter ================================================== File: ./web/assets/IBM Plex Mono Inter/Inter/Inter-VariableFont_slnt,wght.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xeb in position 17: invalid continuation byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/README.txt -------------------------------------------------- Inter Variable Font =================== This download contains Inter as both a variable font and static fonts. Inter is a variable font with these axes: slnt wght This means all the styles are contained in a single file: Inter/Inter-VariableFont_slnt,wght.ttf If your app fully supports variable fonts, you can now pick intermediate styles that aren’t available as static fonts. Not all apps support variable fonts, and in those cases you can use the static font files for Inter: Inter/static/Inter-Thin.ttf Inter/static/Inter-ExtraLight.ttf Inter/static/Inter-Light.ttf Inter/static/Inter-Regular.ttf Inter/static/Inter-Medium.ttf Inter/static/Inter-SemiBold.ttf Inter/static/Inter-Bold.ttf Inter/static/Inter-ExtraBold.ttf Inter/static/Inter-Black.ttf Get started ----------- 1. Install the font files you want to use 2. Use your app's font picker to view the font family and all the available styles Learn more about variable fonts ------------------------------- https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts https://variablefonts.typenetwork.com https://medium.com/variable-fonts In desktop apps https://theblog.adobe.com/can-variable-fonts-illustrator-cc https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts Online https://developers.google.com/fonts/docs/getting_started https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts Installing fonts MacOS: https://support.apple.com/en-us/HT201749 Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows Android Apps https://developers.google.com/fonts/docs/android https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts License ------- Please read the full license text (OFL.txt) to understand the permissions, restrictions and requirements for usage, redistribution, and modification. You can use them in your products & projects – print or digital, commercial or otherwise. This isn't legal advice, please consider consulting a lawyer and see the full license for all details. -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/OFL.txt -------------------------------------------------- Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: https://openfontlicense.org ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------- Directory: assets/IBM Plex Mono Inter/Inter/static ================================================== File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-ExtraBold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Light.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Medium.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-SemiBold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Bold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-ExtraLight.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Thin.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Regular.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono Inter/Inter/static/Inter-Black.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0xba in position 19: invalid start byte] -------------------------------------------------- Directory: assets/mockup ================================================== File: ./web/assets/mockup/image.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/mockup/Slide_01.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- Directory: assets/IBM Plex Mono ================================================== File: ./web/assets/IBM Plex Mono/IBMPlexMono-ThinItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-LightItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-SemiBoldItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Italic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Bold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Thin.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Light.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Regular.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-Medium.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-MediumItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/OFL.txt -------------------------------------------------- Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: https://openfontlicense.org ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-ExtraLightItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-BoldItalic.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9c in position 19: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-ExtraLight.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- File: ./web/assets/IBM Plex Mono/IBMPlexMono-SemiBold.ttf -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x82 in position 17: invalid start byte] -------------------------------------------------- Directory: assets/icons ================================================== File: ./web/assets/icons/Icon.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/Icon_Dark_BG_Base.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/icons/Icon_Base.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/Icon_Dark_BG.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/icons/Square_Logo.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/icons/Slide_E.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/icons/Icon_Dark_BG.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/Icon_Dark_BG_Base.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/emblem-dark-bg.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/Gradient.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- File: ./web/assets/icons/emblem-light-bg.svg -------------------------------------------------- -------------------------------------------------- File: ./web/assets/icons/Circular.png -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte] -------------------------------------------------- Directory: assets/js ================================================== File: ./web/assets/js/api-client.js -------------------------------------------------- /** * DNAnalyzer API Client * * This module provides functions to interact with the DNAnalyzer API endpoints. * It enables the website to access the core DNAnalyzer features through a unified interface. * * @version 1.0.0 */ const DNAnalyzerAPI = { // Base URL for API requests - configurable for different environments baseUrl: 'http://localhost:8080/api/v1', /** * Set the base URL for the API * @param {string} url - The base URL for the API */ setBaseUrl(url) { this.baseUrl = url; }, /** * Check if the API is online * @returns {Promise} - API status information */ async checkStatus() { try { const response = await fetch(`${this.baseUrl}/status`); if (!response.ok) { throw new Error('API status check failed'); } return await response.json(); } catch (error) { console.error('API status check error:', error); throw error; } }, /** * Analyze a DNA file with various options * @param {File} dnaFile - The DNA file to analyze * @param {Object} options - Analysis options * @returns {Promise} - Analysis results */ async analyzeDNA(dnaFile, options = {}) { try { const formData = new FormData(); formData.append('dnaFile', dnaFile); // Add options to form data if (options.amino) formData.append('amino', options.amino); if (options.minCount) formData.append('minCount', options.minCount); if (options.maxCount) formData.append('maxCount', options.maxCount); if (options.reverse) formData.append('reverse', options.reverse); if (options.rcomplement) formData.append('rcomplement', options.rcomplement); if (options.codons) formData.append('codons', options.codons); if (options.coverage) formData.append('coverage', options.coverage); if (options.longest) formData.append('longest', options.longest); if (options.format) formData.append('format', options.format); const response = await fetch(`${this.baseUrl}/analyze`, { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'DNA analysis failed'); } // Handle different response formats const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } else { return await response.text(); } } catch (error) { console.error('DNA analysis error:', error); throw error; } }, /** * Analyze base pair composition of a DNA sequence * @param {string} sequence - The DNA sequence * @returns {Promise} - Base pair analysis results */ async analyzeBasePairs(sequence) { try { const response = await fetch(`${this.baseUrl}/base-pairs`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sequence }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Base pair analysis failed'); } return await response.json(); } catch (error) { console.error('Base pair analysis error:', error); throw error; } }, /** * Find proteins in a DNA sequence * @param {string} sequence - The DNA sequence * @param {string} aminoAcid - The amino acid to start proteins with * @returns {Promise} - Protein analysis results */ async findProteins(sequence, aminoAcid = 'M') { try { const response = await fetch(`${this.baseUrl}/find-proteins`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sequence, aminoAcid }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Protein finding failed'); } return await response.json(); } catch (error) { console.error('Protein finding error:', error); throw error; } }, /** * Analyze reading frames in a DNA sequence * @param {string} sequence - The DNA sequence * @param {number} minLength - Minimum length for potential genes * @returns {Promise} - Reading frame analysis results */ async analyzeReadingFrames(sequence, minLength = 300) { try { const response = await fetch(`${this.baseUrl}/reading-frames`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sequence, minLength }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Reading frame analysis failed'); } return await response.json(); } catch (error) { console.error('Reading frame analysis error:', error); throw error; } }, /** * Analyze genetic testing data (23andMe, AncestryDNA, etc.) * @param {File} geneticFile - The genetic data file * @param {boolean} snpAnalysis - Whether to include detailed SNP analysis * @returns {Promise} - Genetic analysis results */ async analyzeGeneticData(geneticFile, snpAnalysis = false) { try { const formData = new FormData(); formData.append('geneticFile', geneticFile); formData.append('snpAnalysis', snpAnalysis); const response = await fetch(`${this.baseUrl}/analyze-genetic`, { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Genetic data analysis failed'); } return await response.json(); } catch (error) { console.error('Genetic data analysis error:', error); throw error; } }, /** * Manipulate a DNA sequence (reverse, complement) * @param {string} sequence - The DNA sequence * @param {boolean} reverse - Whether to reverse the sequence * @param {boolean} complement - Whether to get the complement * @returns {Promise} - Manipulated sequence results */ async manipulateDNA(sequence, reverse = false, complement = false) { try { const response = await fetch(`${this.baseUrl}/manipulate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sequence, reverse, complement }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'DNA manipulation failed'); } return await response.json(); } catch (error) { console.error('DNA manipulation error:', error); throw error; } }, /** * Parse a DNA file (FASTA, FASTQ) * @param {File} file - The file to parse * @returns {Promise} - Parsed DNA sequence */ async parseFile(file) { try { const formData = new FormData(); formData.append('file', file); const response = await fetch(`${this.baseUrl}/parse`, { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'File parsing failed'); } return await response.json(); } catch (error) { console.error('File parsing error:', error); throw error; } } }; // Export the API client for use in other modules if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = DNAnalyzerAPI; } else { window.DNAnalyzerAPI = DNAnalyzerAPI; } -------------------------------------------------- Directory: assets/videos ================================================== File: ./web/assets/videos/dna_spiral.mp4 -------------------------------------------------- [Could not read file: 'utf-8' codec can't decode byte 0x9b in position 35: invalid start byte] -------------------------------------------------- Directory: features ================================================== File: ./web/features/features.css -------------------------------------------------- /* Features Page Styles */ /* Hero section */ .features-hero { padding: 140px 0 80px; position: relative; text-align: center; } .features-hero-content h1 { font-size: 3.5rem; margin-bottom: var(--space-md); } .features-hero-content p { font-size: 1.2rem; max-width: 700px; margin: 0 auto; color: rgba(255, 255, 255, 0.8); } /* Feature highlights */ .feature-highlights { padding-top: var(--space-xxl); } .features-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--space-xl); } .feature-card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); padding: var(--space-xl); border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-normal); height: 100%; display: flex; flex-direction: column; } .feature-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: var(--shadow-md); border-color: rgba(255, 255, 255, 0.15); } .feature-card .feature-icon { width: 60px; height: 60px; background: linear-gradient(135deg, rgba(255, 0, 102, 0.15), rgba(0, 164, 239, 0.15)); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: var(--blue); margin-bottom: var(--space-md); } .feature-card h3 { font-size: 1.3rem; margin-bottom: var(--space-sm); color: var(--white); } .feature-card p { color: rgba(255, 255, 255, 0.7); flex: 1; margin-bottom: var(--space-md); } .feature-link { display: inline-flex; align-items: center; gap: var(--space-xs); color: var(--blue); font-weight: 500; font-size: 0.95rem; transition: all var(--transition-fast); } .feature-link i { transition: all var(--transition-fast); } .feature-link:hover { color: var(--light-blue); } .feature-link:hover i { transform: translateX(var(--space-xs)); } /* Detailed feature sections */ .section-detailed { padding: var(--space-xxl) 0; } .feature-section-odd { background: linear-gradient(135deg, rgba(5, 30, 62, 0.7), rgba(0, 20, 39, 0.7)); } .feature-detailed { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xxl); align-items: center; } .feature-detailed.reverse { grid-template-columns: 1fr 1fr; direction: rtl; } .feature-detailed.reverse .feature-content { direction: ltr; } .feature-image { position: relative; } .feature-image-wrapper { position: relative; overflow: hidden; border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); background: linear-gradient(135deg, rgba(255, 0, 102, 0.1), rgba(0, 164, 239, 0.1)); padding: var(--space-sm); height: 0; padding-bottom: 70%; display: flex; align-items: center; justify-content: center; } .feature-image-wrapper::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, rgba(255, 0, 102, 0.05), rgba(0, 164, 239, 0.05)); z-index: 0; } .feature-image-wrapper img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; transition: all var(--transition-normal); } .feature-content h2 { font-size: 2.5rem; margin-bottom: var(--space-md); color: var(--white); } .feature-content > p { font-size: 1.1rem; color: rgba(255, 255, 255, 0.8); margin-bottom: var(--space-lg); } .feature-list { list-style: none; padding: 0; margin: 0 0 var(--space-xl) 0; } .feature-list li { display: flex; margin-bottom: var(--space-lg); gap: var(--space-md); } .feature-list li i { color: var(--blue); font-size: 1.2rem; flex-shrink: 0; margin-top: 0.2rem; } .feature-list li div h4 { margin: 0 0 var(--space-xs) 0; font-size: 1.1rem; color: var(--white); } .feature-list li div p { margin: 0; color: rgba(255, 255, 255, 0.7); } /* Comparison table */ .comparison-section { background: var(--dark-blue); padding: var(--space-xxl) 0; } .comparison-table-container { overflow-x: auto; margin-top: var(--space-xl); } .comparison-table { width: 100%; border-collapse: collapse; color: var(--white); } .comparison-table th, .comparison-table td { padding: var(--space-md) var(--space-lg); text-align: left; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .comparison-table th { background: rgba(0, 0, 0, 0.2); font-weight: 600; color: var(--blue); } .comparison-table tr:hover td { background: rgba(255, 255, 255, 0.05); } .comparison-table td:nth-child(1) { font-weight: 500; } .check-icon { color: var(--success); margin-right: var(--space-xs); } .x-icon { color: var(--error); margin-right: var(--space-xs); } .minus-icon { color: var(--warning); margin-right: var(--space-xs); } /* Testimonials */ .testimonials-section { padding: var(--space-xxl) 0; } .testimonials-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--space-xl); margin-top: var(--space-xl); } .testimonial-card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-lg); padding: var(--space-xl); border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-normal); height: 100%; display: flex; flex-direction: column; } .testimonial-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: var(--shadow-md); border-color: rgba(255, 255, 255, 0.15); } .testimonial-content { flex: 1; } .testimonial-content p { font-style: italic; color: rgba(255, 255, 255, 0.85); font-size: 1.05rem; line-height: 1.6; position: relative; padding: 0 var(--space-sm); } .testimonial-content p::before, .testimonial-content p::after { content: '"'; color: var(--blue); font-size: 1.5rem; font-weight: 700; position: absolute; } .testimonial-content p::before { left: -var(--space-xs); top: -var(--space-xs); } .testimonial-content p::after { right: -var(--space-xs); bottom: 0; } .testimonial-author { display: flex; align-items: center; margin-top: var(--space-md); padding-top: var(--space-md); border-top: 1px solid rgba(255, 255, 255, 0.1); } .author-info h4 { margin: 0 0 var(--space-xs) 0; font-size: 1rem; color: var(--white); } .author-info p { margin: 0; font-size: 0.85rem; color: rgba(255, 255, 255, 0.6); } /* CTA Section */ .cta-section { background: linear-gradient(135deg, rgba(255, 0, 102, 0.1), rgba(0, 164, 239, 0.1)); padding: var(--space-xxl) 0; text-align: center; } .cta-section h2 { font-size: 2.5rem; margin-bottom: var(--space-md); color: var(--white); } .cta-section p { font-size: 1.2rem; color: rgba(255, 255, 255, 0.8); margin-bottom: var(--space-xl); max-width: 700px; margin-left: auto; margin-right: auto; } .cta-buttons { display: flex; gap: var(--space-md); justify-content: center; } /* Animation for fade-in elements */ [data-aos] { opacity: 0; transform: translateY(20px); transition: opacity 0.8s ease, transform 0.8s ease; } [data-aos].aos-animate { opacity: 1; transform: translateY(0); } [data-aos-delay="100"] { transition-delay: 0.1s; } [data-aos-delay="200"] { transition-delay: 0.2s; } /* Responsive styles */ @media (max-width: 1024px) { .feature-detailed, .feature-detailed.reverse { grid-template-columns: 1fr; gap: var(--space-xl); direction: ltr; } .feature-content h2 { font-size: 2rem; } .cta-buttons { flex-direction: column; max-width: 300px; margin: 0 auto; } } @media (max-width: 768px) { .features-hero-content h1 { font-size: 2.5rem; } .features-grid { grid-template-columns: 1fr; } .testimonials-grid { grid-template-columns: 1fr; } .feature-image-wrapper { padding-bottom: 60%; } } @media (max-width: 480px) { .features-hero-content h1 { font-size: 2rem; } .features-hero-content p { font-size: 1rem; } .feature-content h2 { font-size: 1.75rem; } .feature-list li { flex-direction: column; gap: var(--space-xs); } .comparison-table th, .comparison-table td { padding: var(--space-sm); } } -------------------------------------------------- File: ./web/features/features.html -------------------------------------------------- Features - DNAnalyzer
Exclusive WSSEF Preview:  Transformer Architecture in Epigenomics! Explore Now

DNAnalyzer Features

Advanced DNA analysis capabilities powered by machine learning, designed for researchers, educators, and enthusiasts.

Key Capabilities

Our comprehensive suite of DNA analysis tools provides insights from basic sequence statistics to advanced protein prediction

Sequence Analysis

Get detailed statistics about your DNA sequence, including length, GC content, and nucleotide composition with intuitive visualizations.

Learn more

Codon Detection

Identify start and stop codons across your sequence to locate potential protein-coding regions with high precision.

Learn more

Reading Frames

Analyze all six reading frames to identify potential protein-coding regions and open reading frames (ORFs).

Learn more

Coverage Analysis

Identify high-coverage (GC-rich) regions, often associated with gene-dense areas and promoter regions.

Learn more

Protein Prediction

Predict potential proteins from your DNA sequence and analyze their amino acid composition.

Learn more

Local Server

Deploy DNAnalyzer as a local server for secure, high-throughput analysis of sensitive genomic data.

Learn more
Sequence Analysis

Sequence Analysis

Our basic sequence analysis tools give you immediate insights into your DNA sequence properties.

  • Base Composition

    Interactive visualization of nucleotide distribution with precise percentages

  • GC Content Calculation

    Identify regions with high GC content, important for gene density and stability

  • Sequence Length Analysis

    Get exact measurements of your DNA sequence with support for very large genomes

Try Sequence Analysis
Codon Detection

Codon Detection

Identify critical codon patterns in your DNA sequence with our advanced detection algorithms.

  • Start Codon Identification

    Automatically detect ATG start codons that initiate protein translation

  • Stop Codon Recognition

    Find all TAA, TAG, and TGA stop codons that terminate protein synthesis

  • Codon Frequency Analysis

    Analyze codon usage patterns across your sequence to identify potential gene regions

Try Codon Detection
Reading Frames

Reading Frames Analysis

Comprehensive analysis of all six reading frames to identify potential genes and coding regions.

  • Six-Frame Translation

    Analyze three forward and three reverse reading frames simultaneously

  • Open Reading Frame (ORF) Detection

    Identify complete ORFs from start to stop codons across all frames

  • Gene Prediction

    Find potential genes based on ORF length and composition characteristics

Try Reading Frames Analysis
Coverage Analysis

Coverage Analysis

Identify regions of interest with our powerful coverage analysis tools to spot regulatory elements and gene-dense areas.

  • GC-Rich Region Detection

    Find CpG islands and GC-rich regions often associated with gene promoters

  • Promoter Element Identification

    Detect TATA boxes, BRE, INR, and DPE motifs that indicate promoter regions

  • Visualization Tools

    Graphical representation of coverage across your sequence for easy interpretation

Try Coverage Analysis
Protein Prediction

Protein Prediction

Translate DNA sequences into proteins and analyze their properties with our advanced prediction tools.

  • ORF Translation

    Convert open reading frames to amino acid sequences using the standard genetic code

  • Protein Length Filtering

    Focus on proteins of significant length to identify functional gene products

  • Amino Acid Composition

    Analyze the distribution of amino acids within predicted proteins

Try Protein Prediction
Local Server

Local Server Deployment

Run DNAnalyzer as a local server for secure, high-throughput analysis of sensitive genomic data.

  • Privacy-Focused Analysis

    Keep sensitive genetic data on your own infrastructure with no external data transfer

  • High-Performance Processing

    Optimized for multi-core systems to handle large datasets and batch processing

  • API Integration

    RESTful API for seamless integration with your existing bioinformatics pipelines

Learn About Server Setup

Why Choose DNAnalyzer?

See how DNAnalyzer compares to traditional DNA analysis tools

Feature DNAnalyzer Traditional Tools
User Interface Modern web interface Often command-line only
Privacy On-device processing May require server upload
Machine Learning Advanced ML algorithms Often rule-based only
Setup Complexity Simple, instant setup Complex dependencies
Visualization Interactive graphics Often text-only output
Multiple File Support FASTA, FASTQ, plain text Various formats
Cost Free, open-source Often commercial

What Our Users Say

Hear from researchers and educators who use DNAnalyzer

"DNAnalyzer has transformed how I teach genomics to undergraduates. The visual interface makes complex concepts accessible, and students can perform real analysis without a steep learning curve."

Dr. Sarah Chen

Associate Professor, Molecular Biology

"As a researcher focusing on genetic disorders, I appreciate DNAnalyzer's privacy-first approach. The local server option allows me to analyze sensitive patient data without exposing it to third parties."

Dr. Michael Rodriguez

Genetic Researcher

"The ability to quickly identify promoter regions and protein-coding sequences has accelerated my synthetic biology project significantly. DNAnalyzer is now a core part of our design verification process."

Emily Takahashi

Synthetic Biology Engineer

Ready to start analyzing DNA sequences?

Experience the power of DNAnalyzer's features today

-------------------------------------------------- File: ./web/features/features.js -------------------------------------------------- /** * DNAnalyzer - Features Page JavaScript * Handles animations, scroll events and interactivity */ document.addEventListener('DOMContentLoaded', function() { // Initialize navbar initializeNavbar(); // Initialize animations initializeAnimations(); // Initialize smooth scrolling initializeSmoothScroll(); }); /** * Initialize navbar functionality */ function initializeNavbar() { const navbar = document.getElementById('navbar'); const mobileToggle = document.getElementById('mobileToggle'); const navLinks = document.getElementById('navLinks'); // Handle scroll event to change navbar style window.addEventListener('scroll', function() { if (window.scrollY > 50) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }); // Handle mobile menu toggle if (mobileToggle && navLinks) { mobileToggle.addEventListener('click', function() { navLinks.classList.toggle('active'); // Change the icon based on the state const icon = mobileToggle.querySelector('i'); if (navLinks.classList.contains('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 clicking a link const links = navLinks.querySelectorAll('a'); links.forEach(link => { link.addEventListener('click', function() { navLinks.classList.remove('active'); const icon = mobileToggle.querySelector('i'); icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); }); }); } } /** * Initialize animations for elements */ function initializeAnimations() { // Get all elements with data-aos attribute const animatedElements = document.querySelectorAll('[data-aos]'); // Create Intersection Observer for animations const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('aos-animate'); observer.unobserve(entry.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -100px 0px' }); // Observe all animated elements animatedElements.forEach(element => { observer.observe(element); }); // Add parallax effect to feature images const featureImages = document.querySelectorAll('.feature-image-wrapper'); window.addEventListener('scroll', () => { featureImages.forEach(image => { const rect = image.getBoundingClientRect(); const isVisible = ( rect.top <= window.innerHeight && rect.bottom >= 0 ); if (isVisible) { const scrollPos = window.scrollY; const imgOffset = rect.top + scrollPos; const parallaxOffset = (scrollPos - imgOffset) * 0.1; // Apply parallax effect image.querySelector('img').style.transform = `translateY(${parallaxOffset}px)`; } }); }); } /** * Initialize smooth scrolling for anchor links */ function initializeSmoothScroll() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { const targetId = this.getAttribute('href'); // Skip if it's just "#" or not an ID selector if (targetId === '#' || !targetId.startsWith('#')) return; const targetElement = document.querySelector(targetId); if (targetElement) { e.preventDefault(); const navbarHeight = document.querySelector('.navbar').offsetHeight; const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - navbarHeight - 20; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); } /** * Add hover effects for feature cards */ document.querySelectorAll('.feature-card').forEach(card => { card.addEventListener('mouseenter', function() { const icon = this.querySelector('.feature-icon'); if (icon) { icon.style.transform = 'scale(1.1)'; } }); card.addEventListener('mouseleave', function() { const icon = this.querySelector('.feature-icon'); if (icon) { icon.style.transform = 'scale(1)'; } }); }); /** * Add hover effects for comparison table rows */ document.querySelectorAll('.comparison-table tr').forEach(row => { row.addEventListener('mouseenter', function() { const cells = this.querySelectorAll('td'); cells.forEach(cell => { cell.style.transition = 'background-color 0.3s ease'; }); }); }); /** * Add animation for CTA buttons */ document.querySelectorAll('.cta-buttons .btn').forEach(button => { button.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-5px)'; }); button.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); }); // Add scale effect for feature images on hover document.querySelectorAll('.feature-image').forEach(image => { image.addEventListener('mouseenter', function() { const img = this.querySelector('img'); if (img) { img.style.transform = 'scale(1.03)'; } }); image.addEventListener('mouseleave', function() { const img = this.querySelector('img'); if (img) { img.style.transform = 'scale(1)'; } }); }); -------------------------------------------------- Directory: hiring ================================================== File: ./web/hiring/hiring.html -------------------------------------------------- Hiring - DNAnalyzer

Join Our Team

We're looking for talented individuals to help us revolutionize DNA analysis.

Open Positions

Software Engineer

Develop and maintain our DNA analysis tools and infrastructure.

Apply Now

Data Scientist

Analyze genetic data and develop machine learning models for DNA analysis.

Apply Now

UI/UX Designer

Design intuitive and engaging user interfaces for our web and mobile applications.

Apply Now

Why Work With Us?

Innovative Projects

Work on cutting-edge technology and make a real impact in the field of genomics.

Collaborative Environment

Join a team of passionate professionals who are dedicated to advancing DNA analysis.

Comprehensive Benefits

Enjoy competitive salaries, health insurance, and other great perks.

-------------------------------------------------- File: ./web/hiring/hiring.css -------------------------------------------------- /* General Styles */ body { font-family: 'IBM Plex Mono', monospace; background-color: #0a0c1b; color: #ffffff; line-height: 1.6; margin: 0; padding: 0; } /* Container */ .container { max-width: 1200px; margin: 6rem auto 2rem; padding: 2rem; } /* Hero Section */ .hero { text-align: center; margin-bottom: 4rem; } .hero h1 { font-size: 3.5rem; margin-bottom: 1.5rem; background: linear-gradient(45deg, var(--gradient-start), var(--gradient-end)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .hero p { font-size: 1.2rem; color: rgba(255, 255, 255, 0.9); max-width: 800px; margin: 0 auto; line-height: 1.8; } /* Positions Section */ .positions-section { margin: 4rem 0; padding: 2rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } .positions-section h2 { font-size: 2.5rem; margin-bottom: 2rem; text-align: center; color: var(--gradient-end); } .positions-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 2rem; margin-top: 3rem; } .position-card { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 2rem; text-align: center; border: 1px solid rgba(255, 255, 255, 0.1); transition: all 0.3s ease; } .position-card:hover { transform: translateY(-5px); border-color: var(--gradient-end); box-shadow: 0 4px 20px rgba(0, 122, 255, 0.2); } .position-card h3 { font-size: 1.5rem; margin-bottom: 1rem; color: var(--gradient-end); } .position-card p { color: rgba(255, 255, 255, 0.9); font-size: 1rem; line-height: 1.6; margin-bottom: 1.5rem; } .primary-btn { background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); color: white; border: none; padding: 0.8rem 1.6rem; border-radius: 8px; font-size: 0.95rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; min-width: 160px; text-decoration: none; text-align: center; display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; } .primary-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0, 122, 255, 0.4); filter: brightness(1.1); } /* Benefits Section */ .benefits-section { margin: 4rem 0; padding: 2rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } .benefits-section h2 { font-size: 2.5rem; margin-bottom: 2rem; text-align: center; color: var(--gradient-end); } .benefits-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 2rem; margin-top: 3rem; } .benefit-card { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 2rem; text-align: center; border: 1px solid rgba(255, 255, 255, 0.1); transition: all 0.3s ease; } .benefit-card:hover { transform: translateY(-5px); border-color: var(--gradient-end); box-shadow: 0 4px 20px rgba(0, 122, 255, 0.2); } .benefit-icon { font-size: 2.5rem; margin-bottom: 1rem; color: var(--gradient-end); } .benefit-card h3 { font-size: 1.5rem; margin-bottom: 1rem; color: var(--gradient-end); } .benefit-card p { color: rgba(255, 255, 255, 0.9); font-size: 1rem; line-height: 1.6; } /* Responsive Design */ @media screen and (max-width: 768px) { .container { margin: 4rem 1rem 2rem; padding: 1rem; } .hero h1 { font-size: 2.5rem; } .hero p { font-size: 1.1rem; } .positions-section, .benefits-section { padding: 1.5rem; } .positions-grid, .benefits-grid { grid-template-columns: 1fr; } } -------------------------------------------------- File: ./web/hiring/hiring-modal.css -------------------------------------------------- /* Modal Container */ .modal-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; } .modal-container.active { opacity: 1; visibility: visible; } /* Modal Content */ .modal-content { background: var(--surface-color); padding: 2rem; border-radius: 12px; max-width: 600px; width: 100%; position: relative; box-shadow: 0 8px 32px rgba(0, 122, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); animation: slideIn 0.3s ease; } @keyframes slideIn { from { transform: translateY(-20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } /* Close Button */ .close-modal { position: absolute; top: 1rem; right: 1rem; background: none; border: none; font-size: 1.5rem; color: rgba(255, 255, 255, 0.7); cursor: pointer; transition: color 0.3s ease; } .close-modal:hover { color: rgba(255, 255, 255, 1); } /* Form Styles */ .form-group { margin-bottom: 1.5rem; } .form-group label { display: block; margin-bottom: 0.5rem; color: rgba(255, 255, 255, 0.9); font-size: 1.1rem; } .form-group input, .form-group select, .form-group textarea { width: 100%; padding: 1rem; background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; color: white; font-size: 1rem; font-family: 'IBM Plex Mono', monospace; } .form-group input:focus, .form-group select:focus, .form-group textarea:focus { outline: none; border-color: var(--gradient-start); box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2); } /* Submit Button */ .primary-btn { background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); color: white; border: none; padding: 0.8rem 1.6rem; border-radius: 8px; font-size: 0.95rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; min-width: 160px; text-decoration: none; text-align: center; display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; } .primary-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0, 122, 255, 0.4); filter: brightness(1.1); } -------------------------------------------------- File: ./web/hiring/hiring-modal.js -------------------------------------------------- document.addEventListener('DOMContentLoaded', function() { const modalContainer = document.querySelector('.modal-container'); const closeModalButton = document.querySelector('.close-modal'); const applicationForm = document.getElementById('application-form'); // Function to open the modal function openModal() { modalContainer.classList.add('active'); } // Function to close the modal function closeModal() { modalContainer.classList.remove('active'); } // Event listener for close button closeModalButton.addEventListener('click', closeModal); // Event listener for form submission applicationForm.addEventListener('submit', function(event) { event.preventDefault(); // Handle form submission logic here alert('Application submitted successfully!'); closeModal(); }); // Open the modal when the page loads openModal(); }); -------------------------------------------------- File: ./web/hiring/hiring.js -------------------------------------------------- document.addEventListener('DOMContentLoaded', function() { // Add smooth scrolling for any anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); document.querySelector(this.getAttribute('href')).scrollIntoView({ behavior: 'smooth' }); }); }); // Add hover effects for position cards document.querySelectorAll('.position-card').forEach(card => { card.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-5px)'; this.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.2)'; this.style.borderColor = 'var(--gradient-end)'; }); card.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = 'none'; this.style.borderColor = 'rgba(255, 255, 255, 0.1)'; }); }); // Add hover effects for benefit cards document.querySelectorAll('.benefit-card').forEach(card => { card.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-5px)'; this.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.2)'; this.style.borderColor = 'var(--gradient-end)'; }); card.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = 'none'; this.style.borderColor = 'rgba(255, 255, 255, 0.1)'; }); }); }); -------------------------------------------------- File: ./web/hiring/hiring-modal.html -------------------------------------------------- Apply Now - DNAnalyzer -------------------------------------------------- Directory: server ================================================== File: ./web/server/server.js -------------------------------------------------- /* * DNAnalyzer Server Page JavaScript * Copyright © 2025 Piyush Acharya */ document.addEventListener('DOMContentLoaded', function() { // Mobile navigation toggle const mobileToggle = document.getElementById('mobileToggle'); const navLinks = document.getElementById('navLinks'); if (mobileToggle && navLinks) { mobileToggle.addEventListener('click', function() { navLinks.classList.toggle('active'); mobileToggle.querySelector('i').classList.toggle('fa-bars'); mobileToggle.querySelector('i').classList.toggle('fa-times'); }); } // Navbar scroll behavior const navbar = document.getElementById('navbar'); window.addEventListener('scroll', function() { if (window.scrollY > 100) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }); // Server status check const serverStatusElement = document.getElementById('server-status'); let statusCheckInterval; let retryCount = 0; const MAX_RETRIES = 3; const SERVER_PORT = 8080; // Copy commands functionality window.copyCloneCommand = function() { copyToClipboard('git clone https://github.com/VerisimilitudeX/DNAnalyzer.git && cd DNAnalyzer', 0); }; window.copyChmodCommand = function() { copyToClipboard('chmod +x ./gradlew', 1); }; window.copyBuildCommand = function() { copyToClipboard('./gradlew clean bootJar', 2); }; window.copyRunCommand = function() { copyToClipboard('java -jar build/libs/DNAnalyzer.jar', 3); }; function copyToClipboard(text, buttonIndex) { navigator.clipboard.writeText(text).then(() => { const buttons = document.querySelectorAll('.copy-button'); const button = buttons[buttonIndex]; button.textContent = 'Copied!'; button.classList.add('success'); setTimeout(() => { button.textContent = 'Copy'; button.classList.remove('success'); }, 2000); }).catch(() => { const buttons = document.querySelectorAll('.copy-button'); const button = buttons[buttonIndex]; button.textContent = 'Failed'; button.classList.add('error'); setTimeout(() => { button.textContent = 'Copy'; button.classList.remove('error'); }, 2000); }); } // Server status check with retry logic async function checkServerStatus(isInitialCheck = false) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); // Try the API status endpoint first const response = await fetch(`http://localhost:${SERVER_PORT}/api/v1/status`, { method: 'GET', signal: controller.signal, headers: { 'Accept': 'application/json', } }); clearTimeout(timeoutId); retryCount = 0; // Reset retry count on success serverStatusElement.innerHTML = `
Server Online
Status: Running Last checked: ${new Date().toLocaleTimeString()} Ready to process DNA sequences
`; return true; } catch (error) { // Try root URL as fallback try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const response = await fetch(`http://localhost:${SERVER_PORT}/`, { method: 'GET', signal: controller.signal }); clearTimeout(timeoutId); if (response.ok || response.status === 404) { // Even a 404 means the server is running retryCount = 0; serverStatusElement.innerHTML = `
Server Online
Status: Running Last checked: ${new Date().toLocaleTimeString()} Ready to process DNA sequences
`; return true; } throw new Error('Server not responding correctly'); } catch (fallbackError) { retryCount++; const isMaxRetries = retryCount >= MAX_RETRIES; serverStatusElement.innerHTML = `
Server Offline
Last checked: ${new Date().toLocaleTimeString()} ${isInitialCheck ? 'Follow the setup steps above to start the server' : ''} ${!isInitialCheck && !isMaxRetries ? `Retrying... (${retryCount}/${MAX_RETRIES})` : ''} ${isMaxRetries ? 'Max retries reached. Check server setup instructions above.' : ''}
`; if (isMaxRetries) { clearInterval(statusCheckInterval); showTroubleshooting(); } return false; } } } function showTroubleshooting() { const troubleshootingSection = document.getElementById('troubleshooting'); if (troubleshootingSection) { troubleshootingSection.classList.add('active'); troubleshootingSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } // Initialize status checking async function initializeStatusCheck() { const isOnline = await checkServerStatus(true); if (!isOnline) { // If offline on first check, start checking more frequently initially statusCheckInterval = setInterval(() => checkServerStatus(), 5000); // After 30 seconds, switch to normal interval if server is still offline setTimeout(() => { clearInterval(statusCheckInterval); statusCheckInterval = setInterval(() => checkServerStatus(), 30000); }, 30000); } else { // If online, check every 30 seconds statusCheckInterval = setInterval(() => checkServerStatus(), 30000); } } // Start status checking initializeStatusCheck(); // Cleanup on page unload window.addEventListener('unload', () => { if (statusCheckInterval) { clearInterval(statusCheckInterval); } }); // Add animated appearance for steps const animateSteps = () => { const stepCards = document.querySelectorAll('.step-card'); const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.style.opacity = 1; entry.target.style.transform = 'translateY(0)'; observer.unobserve(entry.target); } }); }, { threshold: 0.1 }); stepCards.forEach((card, index) => { card.style.opacity = 0; card.style.transform = 'translateY(20px)'; card.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; card.style.transitionDelay = `${index * 0.1}s`; observer.observe(card); }); }; // Initialize animations setTimeout(animateSteps, 500); }); -------------------------------------------------- File: ./web/server/server.html -------------------------------------------------- Server - DNAnalyzer

DNAnalyzer Server

Set up and run your own DNAnalyzer server for local development and advanced genomic analysis

Server Status

Check your local server installation status and requirements

Server Status

Checking...

Local Server

System Requirements

2GB+

Minimum RAM

Version

Java 17+

Required Runtime

Quick Start Guide

Follow these steps to set up your local DNAnalyzer server

Clone the Repository

git clone https://github.com/VerisimilitudeX/DNAnalyzer.git && cd DNAnalyzer

Clone the repository and navigate to the project directory

Setup Gradle

chmod +x ./gradlew

Make the Gradle wrapper executable (Unix/macOS systems)

Build the Project

./gradlew clean bootJar

Build the project (this may take a few minutes on first run)

Start the Server

java -jar build/libs/DNAnalyzer.jar

Start the local server on port 8080

Troubleshooting

Common issues and their solutions

Port Already in Use

If port 8080 is already in use, you may see an error like "Address already in use". To fix this:

  • Close any other applications using port 8080
  • Or modify the port in application.yml to use a different port
  • Alternatively, use the command line option to specify a different port
java -jar build/libs/DNAnalyzer.jar --server.port=8090

Java Version Issues

If you see "UnsupportedClassVersionError", make sure you have Java 17+ installed:

java -version

Download and install Java 17 or later if your version is outdated.

Build Failures

If you encounter build errors, try these solutions:

  • Run './gradlew clean' first to clear any cached files
  • Make sure your Java installation is properly configured
  • Ensure you have write permissions in the project directory
  • Update Gradle wrapper if needed with './gradlew wrapper --gradle-version=7.5'

Ready to Contribute?

Join our community and help improve DNAnalyzer's server capabilities.

-------------------------------------------------- File: ./web/server/server.css -------------------------------------------------- /* Server Page Custom Styles */ /* Code blocks for server page */ .code-block { background-color: rgba(0, 0, 0, 0.3); border-radius: var(--radius-md); padding: 1rem; margin: 1rem 0; position: relative; font-family: var(--font-mono); font-size: 0.9rem; overflow-x: auto; color: rgba(255, 255, 255, 0.9); border: 1px solid rgba(255, 255, 255, 0.1); } .copy-button { position: absolute; top: 0.5rem; right: 0.5rem; background: rgba(255, 255, 255, 0.1); border: none; color: rgba(255, 255, 255, 0.7); padding: 0.3rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; transition: all 0.2s ease; } .copy-button:hover { background: rgba(255, 255, 255, 0.2); color: white; } .copy-button.success { background: rgba(0, 200, 83, 0.2); color: #00c853; } .copy-button.error { background: rgba(244, 67, 54, 0.2); color: #f44336; } /* Status Value styling */ .status-value { font-size: 1.8rem; font-weight: 600; margin: 0.5rem 0; color: rgba(255, 255, 255, 0.9); } /* Status indicators */ .status-indicator { display: flex; align-items: center; justify-content: center; gap: 1rem; } .status-dot { width: 16px; height: 16px; border-radius: 50%; } .online .status-dot { background: #00c853; box-shadow: 0 0 10px rgba(0, 200, 83, 0.5); } .offline .status-dot { background: #f44336; box-shadow: 0 0 10px rgba(244, 67, 54, 0.5); } .status-main { font-weight: 600; font-size: 1.2rem; } .online .status-main { color: #00c853; } .offline .status-main { color: #f44336; } .status-info { display: flex; flex-direction: column; font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); margin-top: 0.5rem; } /* Troubleshooting section */ #troubleshooting { opacity: 1; transition: all 0.5s ease; } #troubleshooting.active { animation: pulse 1s ease; } @keyframes pulse { 0% { opacity: 0.7; } 50% { opacity: 1; } 100% { opacity: 0.7; } }5rem; margin-bottom: 1.5rem; } .server-hero-content p { font-size: 1.2rem; color: rgba(255, 255, 255, 0.9); max-width: 800px; margin: 0 auto; line-height: 1.8; } /* Status Cards Section */ .status-section { margin: 4rem 0; padding: 2rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } .status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 2rem; margin-top: 1rem; } .status-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 2rem; text-align: center; transition: all 0.3s ease; } .status-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.15); } .status-icon { font-size: 2rem; margin-bottom: 1rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } .status-card h3 { font-size: 1.3rem; margin-bottom: 1rem; color: var(--white); } .status-value { font-size: 1.8rem; font-weight: 600; margin-bottom: 0.5rem; color: rgba(255, 255, 255, 0.9); } .status-label { color: rgba(255, 255, 255, 0.7); font-size: 0.95rem; margin: 0; } /* Status Indicators */ .status-indicator { display: flex; align-items: center; justify-content: center; gap: 1rem; } .status-dot { width: 16px; height: 16px; border-radius: 50%; } .online .status-dot { background: #00c853; box-shadow: 0 0 10px rgba(0, 200, 83, 0.5); } .offline .status-dot { background: #f44336; box-shadow: 0 0 10px rgba(244, 67, 54, 0.5); } .status-main { font-weight: 600; font-size: 1.2rem; } .online .status-main { color: #00c853; } .offline .status-main { color: #f44336; } .status-info { display: flex; flex-direction: column; font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); margin-top: 0.5rem; } /* Guide Section */ .guide-section { margin: 4rem 0; padding: 2rem; background: rgba(0, 0, 0, 0.2); border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.1); } .guide-section h2 { font-size: 2rem; margin-bottom: 1.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; display: inline-block; } .step-list { display: grid; gap: 1.5rem; } .step-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; display: flex; align-items: flex-start; gap: 1rem; } .step-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.15); } .step-number { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: linear-gradient(135deg, var(--magenta), var(--blue)); color: white; font-weight: 600; flex-shrink: 0; } .step-content { flex-grow: 1; } .step-command { font-family: 'IBM Plex Mono', monospace; font-size: 0.95rem; color: rgba(255, 255, 255, 0.9); margin-bottom: 0.8rem; padding: 0.8rem; background: rgba(0, 0, 0, 0.3); border-radius: 6px; overflow-x: auto; position: relative; } .copy-button { position: absolute; top: 0.5rem; right: 0.5rem; background: rgba(255, 255, 255, 0.1); border: none; color: rgba(255, 255, 255, 0.7); padding: 0.3rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; transition: all 0.2s ease; } .copy-button:hover { background: rgba(255, 255, 255, 0.2); color: white; } .copy-button.success { background: rgba(0, 200, 83, 0.2); color: #00c853; } .copy-button.error { background: rgba(244, 67, 54, 0.2); color: #f44336; } .step-description { color: rgba(255, 255, 255, 0.8); font-size: 0.95rem; line-height: 1.5; margin: 0; } /* Troubleshooting Section */ .troubleshooting-section { margin: 4rem 0; opacity: 0.7; transition: all 0.5s ease; } .troubleshooting-section.active { opacity: 1; } .troubleshooting-section h2 { font-size: 2rem; margin-bottom: 1.5rem; background: linear-gradient(135deg, var(--magenta), var(--blue)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; display: inline-block; } .issue-list { display: grid; gap: 1.5rem; } .issue-card { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; } .issue-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.15); } .issue-card h3 { display: flex; align-items: center; gap: 0.8rem; font-size: 1.3rem; margin-top: 0; margin-bottom: 1rem; } .issue-card h3 i { color: var(--magenta); } .issue-card p { color: rgba(255, 255, 255, 0.8); font-size: 1rem; line-height: 1.6; margin-bottom: 1rem; } .issue-card ul { margin: 0; padding-left: 1.5rem; } .issue-card li { color: rgba(255, 255, 255, 0.8); margin-bottom: 0.5rem; } .issue-command { font-family: 'IBM Plex Mono', monospace; font-size: 0.95rem; color: rgba(255, 255, 255, 0.9); margin: 1rem 0; padding: 0.8rem; background: rgba(0, 0, 0, 0.3); border-radius: 6px; overflow-x: auto; } /* Responsive Design */ @media screen and (max-width: 992px) { .server-hero-content h1 { font-size: 3rem; } } @media screen and (max-width: 768px) { .container { margin: 4rem 1rem 2rem; padding: 1rem; } .server-hero-content h1 { font-size: 2.5rem; } .server-hero-content p { font-size: 1.1rem; } .status-grid { grid-template-columns: 1fr; } .step-command { font-size: 0.85rem; } } @media screen and (max-width: 480px) { .server-hero-content h1 { font-size: 2rem; } .status-card { padding: 1.5rem; } .step-card { flex-direction: column; } .step-number { margin-bottom: 0.5rem; } } -------------------------------------------------- Directory: docs ================================================== File: ./web/docs/docs.js -------------------------------------------------- /** * DNAnalyzer - Documentation Page JavaScript * Handles navigation, search, and interactive elements */ document.addEventListener('DOMContentLoaded', function() { // Initialize mobile navigation initMobileNav(); // Initialize smooth scrolling initSmoothScroll(); // Initialize tabs initTabs(); // Initialize code copy buttons initCodeCopy(); // Initialize FAQ accordions initFaqAccordions(); // Initialize active link tracking initActiveLinkTracking(); // Initialize search functionality initSearch(); }); /** * Initialize mobile navigation */ function initMobileNav() { const sidebar = document.getElementById('docsSidebar'); const sidebarToggle = document.getElementById('sidebarToggle'); const closeSidebar = document.getElementById('closeSidebar'); if (sidebar && sidebarToggle) { // Toggle sidebar on mobile sidebarToggle.addEventListener('click', function() { sidebar.classList.add('active'); }); // Close sidebar on mobile if (closeSidebar) { closeSidebar.addEventListener('click', function() { sidebar.classList.remove('active'); }); } // Close sidebar when clicking on links (mobile) const sidebarLinks = sidebar.querySelectorAll('a'); sidebarLinks.forEach(link => { link.addEventListener('click', function() { if (window.innerWidth <= 768) { sidebar.classList.remove('active'); } }); }); // Close sidebar when clicking outside (mobile) document.addEventListener('click', function(event) { if (window.innerWidth <= 768 && !sidebar.contains(event.target) && event.target !== sidebarToggle && !sidebarToggle.contains(event.target)) { sidebar.classList.remove('active'); } }); } } /** * Initialize smooth scrolling for anchor links */ function initSmoothScroll() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { const targetId = this.getAttribute('href'); // Skip if it's just "#" or not an ID selector if (targetId === '#' || !targetId.startsWith('#')) return; const targetElement = document.querySelector(targetId); if (targetElement) { e.preventDefault(); const navbarHeight = 70; // Height of the fixed navbar const docsHeaderHeight = 50; // Height of the docs header (mobile) const offset = window.innerWidth <= 768 ? navbarHeight + docsHeaderHeight : navbarHeight; const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - offset; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); } /** * Initialize tabs functionality */ function initTabs() { const tabButtons = document.querySelectorAll('.tab-button'); tabButtons.forEach(button => { button.addEventListener('click', function() { const tabId = this.getAttribute('data-tab'); const tabContent = document.getElementById(tabId); // Remove active class from all buttons and contents document.querySelectorAll('.tab-button').forEach(btn => { btn.classList.remove('active'); }); document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); // Add active class to current button and content this.classList.add('active'); if (tabContent) { tabContent.classList.add('active'); } }); }); } /** * Initialize code copy functionality */ function initCodeCopy() { const copyButtons = document.querySelectorAll('.copy-button'); copyButtons.forEach(button => { button.addEventListener('click', function() { const codeBlock = this.closest('.code-block'); const code = codeBlock.querySelector('code').textContent; // Copy to clipboard navigator.clipboard.writeText(code) .then(() => { // Success feedback const originalText = this.textContent; this.textContent = 'Copied!'; this.style.background = 'var(--success)'; // Reset after 2 seconds setTimeout(() => { this.textContent = originalText; this.style.background = ''; }, 2000); }) .catch(err => { console.error('Could not copy text: ', err); // Fallback for older browsers const textarea = document.createElement('textarea'); textarea.value = code; textarea.style.position = 'fixed'; document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { document.execCommand('copy'); // Success feedback const originalText = this.textContent; this.textContent = 'Copied!'; this.style.background = 'var(--success)'; // Reset after 2 seconds setTimeout(() => { this.textContent = originalText; this.style.background = ''; }, 2000); } catch (err) { console.error('Fallback copy failed: ', err); this.textContent = 'Failed!'; this.style.background = 'var(--error)'; setTimeout(() => { this.textContent = 'Copy'; this.style.background = ''; }, 2000); } document.body.removeChild(textarea); }); }); }); } /** * Initialize FAQ accordions */ function initFaqAccordions() { const faqItems = document.querySelectorAll('.faq-item'); faqItems.forEach(item => { const question = item.querySelector('.faq-question'); if (question) { question.addEventListener('click', function() { // Toggle active class on the FAQ item item.classList.toggle('active'); // If this item was activated, close others if (item.classList.contains('active')) { faqItems.forEach(otherItem => { if (otherItem !== item) { otherItem.classList.remove('active'); } }); } }); } }); } /** * Initialize active link tracking based on scroll position */ function initActiveLinkTracking() { const sections = document.querySelectorAll('.doc-section'); const navLinks = document.querySelectorAll('.sidebar-nav a'); if (sections.length === 0 || navLinks.length === 0) return; // Update active link on scroll function updateActiveLink() { let currentSection = ''; const navbarHeight = 70; const docsHeaderHeight = 50; const totalOffset = window.innerWidth <= 768 ? navbarHeight + docsHeaderHeight + 20 : navbarHeight + 20; sections.forEach(section => { const sectionTop = section.offsetTop - totalOffset; const sectionHeight = section.offsetHeight; const sectionId = section.getAttribute('id'); if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) { currentSection = '#' + sectionId; } }); // Update active class on nav links navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === currentSection) { link.classList.add('active'); } }); } // Initial call to set active link on page load updateActiveLink(); // Update active link on scroll window.addEventListener('scroll', updateActiveLink); } /** * Initialize search functionality */ function initSearch() { const searchInput = document.getElementById('docsSearch'); const sections = document.querySelectorAll('.doc-section'); if (!searchInput || sections.length === 0) return; searchInput.addEventListener('input', function() { const query = this.value.trim().toLowerCase(); if (query.length < 2) { // If query is too short, show all sections sections.forEach(section => { section.style.display = 'block'; // Remove any highlights removeHighlights(section); }); return; } // Search and filter sections sections.forEach(section => { const sectionText = section.textContent.toLowerCase(); const headings = Array.from(section.querySelectorAll('h1, h2, h3, h4')).map(h => h.textContent.toLowerCase()); // Check if section contains the query in text or headings const containsQuery = sectionText.includes(query) || headings.some(h => h.includes(query)); if (containsQuery) { section.style.display = 'block'; // Highlight matches removeHighlights(section); highlightText(section, query); } else { section.style.display = 'none'; } }); // If search is cleared, reset highlights if (query.length === 0) { sections.forEach(section => { removeHighlights(section); }); } }); } /** * Highlight matching text in an element * @param {HTMLElement} element - The element to search in * @param {string} query - The text to highlight */ function highlightText(element, query) { // Only highlight text in paragraphs, list items, and code blocks const textNodes = element.querySelectorAll('p, li, code'); textNodes.forEach(node => { const html = node.innerHTML; // Create regex with word boundary for whole words, or without for partial matches const regex = new RegExp(`(\\b${query}\\b|${query})`, 'gi'); const newHtml = html.replace(regex, '$1'); if (newHtml !== html) { node.innerHTML = newHtml; } }); } /** * Remove highlights from an element * @param {HTMLElement} element - The element to remove highlights from */ function removeHighlights(element) { const marks = element.querySelectorAll('mark'); marks.forEach(mark => { // Replace mark with its text content const textNode = document.createTextNode(mark.textContent); mark.parentNode.replaceChild(textNode, mark); }); } -------------------------------------------------- File: ./web/docs/docs.css -------------------------------------------------- /* Documentation Page Styles */ /* Layout */ .docs-layout { display: flex; min-height: calc(100vh - 70px); margin-top: 70px; } /* Sidebar */ .docs-sidebar { width: 300px; background: var(--dark-blue); border-right: 1px solid rgba(255, 255, 255, 0.1); position: fixed; top: 70px; left: 0; bottom: 0; overflow-y: auto; z-index: 100; transition: transform var(--transition-normal); } .sidebar-header { padding: var(--space-lg); border-bottom: 1px solid rgba(255, 255, 255, 0.1); display: flex; justify-content: space-between; align-items: center; } .sidebar-header h2 { margin: 0; font-size: 1.3rem; color: var(--white); } .mobile-close { display: none; background: none; border: none; color: rgba(255, 255, 255, 0.7); font-size: 1.2rem; cursor: pointer; transition: color var(--transition-fast); } .mobile-close:hover { color: var(--white); } .sidebar-search { padding: var(--space-md) var(--space-lg); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .search-input-wrapper { position: relative; } .search-input-wrapper i { position: absolute; left: var(--space-md); top: 50%; transform: translateY(-50%); color: rgba(255, 255, 255, 0.5); pointer-events: none; } .search-input-wrapper input { width: 100%; padding: var(--space-sm) var(--space-sm) var(--space-sm) var(--space-xl); background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: var(--radius-md); color: var(--white); font-size: 0.9rem; transition: all var(--transition-fast); } .search-input-wrapper input:focus { outline: none; border-color: var(--blue); background: rgba(255, 255, 255, 0.15); } .sidebar-nav { padding: var(--space-md) 0; } .nav-section { margin-bottom: var(--space-lg); padding: 0 var(--space-lg); } .nav-section h3 { font-size: 0.9rem; text-transform: uppercase; letter-spacing: 1px; color: rgba(255, 255, 255, 0.5); margin: 0 0 var(--space-sm) 0; } .nav-section ul { list-style: none; padding: 0; margin: 0; } .nav-section li { margin-bottom: 2px; } .nav-section a { display: block; padding: var(--space-xs) 0; color: rgba(255, 255, 255, 0.7); text-decoration: none; font-size: 0.95rem; transition: all var(--transition-fast); border-radius: var(--radius-sm); } .nav-section a:hover { color: var(--white); transform: translateX(2px); } .nav-section a.active { color: var(--blue); font-weight: 500; } /* Content */ .docs-content { flex: 1; margin-left: 300px; position: relative; } .docs-container { max-width: 900px; margin: 0 auto; padding: var(--space-xl) var(--space-xxl) var(--space-xxl); } .docs-mobile-header { display: none; position: sticky; top: 70px; padding: var(--space-md) var(--space-lg); background: var(--dark-blue); border-bottom: 1px solid rgba(255, 255, 255, 0.1); z-index: 90; } .docs-mobile-header h2 { margin: 0; font-size: 1.3rem; color: var(--white); } .sidebar-toggle { display: none; background: none; border: none; color: rgba(255, 255, 255, 0.7); font-size: 1.2rem; cursor: pointer; transition: color var(--transition-fast); margin-right: var(--space-md); } .sidebar-toggle:hover { color: var(--white); } /* Doc Sections */ .doc-section { margin-bottom: var(--space-xxl); scroll-margin-top: 100px; } .doc-section:last-child { margin-bottom: 0; } .doc-section h1 { font-size: 2.5rem; margin-bottom: var(--space-lg); color: var(--white); } .doc-section h2 { font-size: 1.8rem; margin: var(--space-xl) 0 var(--space-md); color: var(--white); border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: var(--space-sm); } .doc-section h3 { font-size: 1.3rem; margin: var(--space-lg) 0 var(--space-sm); color: var(--white); } .doc-section p { margin-bottom: var(--space-md); color: rgba(255, 255, 255, 0.8); font-size: 1rem; line-height: 1.7; } .doc-section p.lead { font-size: 1.1rem; color: rgba(255, 255, 255, 0.9); } .doc-section ul, .doc-section ol { margin-bottom: var(--space-md); color: rgba(255, 255, 255, 0.8); padding-left: var(--space-xl); } .doc-section li { margin-bottom: var(--space-xs); } .doc-section a { color: var(--blue); text-decoration: none; transition: color var(--transition-fast); } .doc-section a:hover { text-decoration: underline; color: var(--light-blue); } .doc-section code { font-family: var(--font-mono); background: rgba(0, 0, 0, 0.2); padding: 2px 5px; border-radius: var(--radius-sm); font-size: 0.9em; color: var(--white); } /* Info Boxes */ .info-box { background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); padding: var(--space-lg); margin: var(--space-lg) 0; border-left: 4px solid var(--blue); } .info-box h3 { margin-top: 0; font-size: 1.1rem; color: var(--white); } .info-box p:last-child, .info-box ul:last-child { margin-bottom: 0; } .info-box.warning { border-left-color: var(--warning); } .info-box.warning h3 { color: var(--warning); } .info-box.error { border-left-color: var(--error); } .info-box.error h3 { color: var(--error); } .info-box.tip { border-left-color: var(--success); } .info-box.tip h3 { color: var(--success); } /* Code Blocks */ .code-block { margin: var(--space-md) 0; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); overflow: hidden; } .code-header { display: flex; justify-content: space-between; align-items: center; background: rgba(0, 0, 0, 0.3); padding: var(--space-sm) var(--space-md); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .code-label { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); } .copy-button { background: rgba(255, 255, 255, 0.1); border: none; color: rgba(255, 255, 255, 0.7); padding: 4px 10px; border-radius: var(--radius-sm); font-size: 0.8rem; cursor: pointer; transition: all var(--transition-fast); } .copy-button:hover { background: rgba(255, 255, 255, 0.2); color: var(--white); } .code-block pre { margin: 0; padding: var(--space-md); overflow-x: auto; } .code-block code { background: transparent; padding: 0; font-family: var(--font-mono); font-size: 0.9rem; line-height: 1.6; color: rgba(255, 255, 255, 0.9); display: block; } /* Tabs */ .tabs { margin: var(--space-md) 0; background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); overflow: hidden; } .tab-header { display: flex; background: rgba(0, 0, 0, 0.3); border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .tab-button { padding: var(--space-sm) var(--space-md); background: transparent; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.7); cursor: pointer; transition: all var(--transition-fast); } .tab-button:hover { color: var(--white); } .tab-button.active { color: var(--blue); border-bottom-color: var(--blue); background: rgba(0, 164, 239, 0.1); } .tab-content { display: none; padding: var(--space-md); } .tab-content.active { display: block; } /* Steps */ .steps { margin: var(--space-lg) 0; } .step { display: flex; margin-bottom: var(--space-lg); gap: var(--space-md); } .step-number { flex-shrink: 0; width: 34px; height: 34px; background: linear-gradient(135deg, var(--magenta), var(--blue)); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 1.1rem; color: var(--white); margin-top: 5px; } .step-content { flex: 1; } .step-content h3 { margin-top: 0; font-size: 1.2rem; margin-bottom: var(--space-sm); } .step-content p { margin-bottom: var(--space-sm); } .step-content p:last-child { margin-bottom: 0; } /* API Endpoints */ .api-endpoint { margin-bottom: var(--space-lg); background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-md); overflow: hidden; } .endpoint-method { display: inline-block; padding: var(--space-xs) var(--space-sm); font-weight: 600; color: var(--white); border-radius: var(--radius-sm); font-size: 0.9rem; margin-right: var(--space-sm); } .endpoint-method.get { background: var(--success); } .endpoint-method.post { background: var(--blue); } .endpoint-method.put { background: var(--warning); } .endpoint-method.delete { background: var(--error); } .endpoint-path { font-family: var(--font-mono); font-size: 1rem; margin-bottom: var(--space-sm); color: var(--white); padding: var(--space-md); background: rgba(0, 0, 0, 0.3); display: flex; align-items: center; } .endpoint-description { padding: var(--space-md); } .endpoint-description p { margin-top: 0; } .endpoint-description h4 { font-size: 1.1rem; margin: var(--space-md) 0 var(--space-xs); color: var(--white); } /* FAQ Section */ .faq-item { margin-bottom: var(--space-md); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--radius-md); overflow: hidden; } .faq-question { padding: var(--space-md); background: rgba(0, 0, 0, 0.2); cursor: pointer; display: flex; justify-content: space-between; align-items: center; } .faq-question h3 { margin: 0; font-size: 1.1rem; color: var(--white); } .faq-question i { color: rgba(255, 255, 255, 0.7); transition: transform var(--transition-fast); } .faq-item.active .faq-question i { transform: rotate(180deg); } .faq-answer { padding: 0 var(--space-md); max-height: 0; overflow: hidden; transition: all var(--transition-normal); } .faq-item.active .faq-answer { padding: var(--space-md); max-height: 1000px; } /* Community Resources */ .community-resources { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--space-lg); margin: var(--space-lg) 0; } .resource-card { background: rgba(255, 255, 255, 0.05); border-radius: var(--radius-md); padding: var(--space-lg); text-align: center; border: 1px solid rgba(255, 255, 255, 0.1); transition: all var(--transition-normal); } .resource-card:hover { transform: translateY(-5px); background: rgba(255, 255, 255, 0.08); box-shadow: var(--shadow-md); border-color: rgba(255, 255, 255, 0.15); } .resource-card i { font-size: 2rem; margin-bottom: var(--space-md); color: var(--blue); } .resource-card h3 { margin-top: 0; margin-bottom: var(--space-sm); font-size: 1.2rem; } .resource-card p { margin-bottom: var(--space-md); color: rgba(255, 255, 255, 0.7); } /* Responsive Design */ @media screen and (max-width: 1024px) { .docs-container { padding: var(--space-xl) var(--space-lg); } .community-resources { grid-template-columns: 1fr; } } @media screen and (max-width: 768px) { .docs-sidebar { transform: translateX(-100%); width: 280px; } .docs-sidebar.active { transform: translateX(0); } .docs-content { margin-left: 0; } .docs-mobile-header { display: flex; align-items: center; } .sidebar-toggle { display: block; } .mobile-close { display: block; } .api-endpoint { overflow-x: auto; } .step { flex-direction: column; gap: var(--space-sm); } .step-number { margin-bottom: var(--space-xs); } } @media screen and (max-width: 480px) { .docs-container { padding: var(--space-lg) var(--space-md); } .doc-section h1 { font-size: 2rem; } .doc-section h2 { font-size: 1.5rem; } .doc-section h3 { font-size: 1.2rem; } .tab-header { flex-wrap: wrap; } .tab-button { flex: 1; min-width: 100px; text-align: center; } } -------------------------------------------------- File: ./web/docs/docs.html -------------------------------------------------- Documentation - DNAnalyzer
Exclusive WSSEF Preview:  Transformer Architecture in Epigenomics! Explore Now

Documentation

Introduction to DNAnalyzer

DNAnalyzer is a powerful, privacy-focused DNA analysis tool using advanced machine learning models for accurate, on-device genomic analysis.

This documentation will guide you through the installation, setup, and usage of DNAnalyzer for various DNA analysis tasks. Whether you're a researcher, educator, or bioinformatics enthusiast, you'll find comprehensive information on all of DNAnalyzer's capabilities.

Key Features

  • Privacy-First Analysis: All processing occurs on your device, ensuring your genetic data never leaves your system.
  • Machine Learning Powered: Advanced ML algorithms provide exceptional accuracy in sequence analysis.
  • Comprehensive Tools: From basic sequence statistics to advanced protein prediction and promoter detection.
  • Open Source: DNAnalyzer is completely open source under the MIT License.

System Requirements

Before getting started, ensure your system meets the following requirements:

System Requirements
- Operating System: Windows 10+, macOS 10.14+, or Linux (Ubuntu 18.04+)
- Memory: 2GB RAM minimum (4GB+ recommended for large sequences)
- Storage: 200MB for application, additional space for sequence files
- Browser: Chrome 80+, Firefox 75+, Safari 13+, Edge 80+ (for web interface)
- Java Runtime: Java 17+ (for server mode)

Installation

DNAnalyzer can be installed and used in multiple ways depending on your needs and technical preferences.

Web Interface (No Installation)

The simplest way to use DNAnalyzer is through our web interface, which requires no installation:

  1. Visit DNAnalyzer Web App
  2. Upload your DNA sequence files
  3. Select analysis options and get results

The web interface runs entirely in your browser, ensuring your data never leaves your device.

Local Server Installation

For more advanced usage or batch processing, you can install DNAnalyzer as a local server:

  1. Download the latest DNAnalyzer release for Windows
  2. Extract the ZIP file to your preferred location
  3. Ensure Java 17 or newer is installed on your system
  4. Double-click on dnanalyzer.bat to start the server
  5. Access the interface at http://localhost:8080 in your browser
  1. Download the latest DNAnalyzer release for macOS
  2. Extract the ZIP file to your preferred location
  3. Ensure Java 17 or newer is installed on your system
  4. Open Terminal and navigate to the extracted folder
  5. Run chmod +x dnanalyzer.sh to make the script executable
  6. Execute ./dnanalyzer.sh to start the server
  7. Access the interface at http://localhost:8080 in your browser
  1. Download the latest DNAnalyzer release for Linux
  2. Extract the archive to your preferred location
  3. Ensure Java 17 or newer is installed on your system
  4. Open Terminal and navigate to the extracted folder
  5. Run chmod +x dnanalyzer.sh to make the script executable
  6. Execute ./dnanalyzer.sh to start the server
  7. Access the interface at http://localhost:8080 in your browser

Building from Source

For developers or those who want the latest features, you can build DNAnalyzer from source:

Building from Source
# Clone the repository
git clone https://github.com/VerisimilitudeX/DNAnalyzer.git
cd DNAnalyzer

# Make the Gradle wrapper executable
chmod +x ./gradlew

# Build the project
./gradlew clean bootJar

# Run the server
java -jar build/libs/DNAnalyzer.jar

Quick Start Guide

This guide will help you quickly get started with DNAnalyzer's basic features.

Analyzing Your First DNA Sequence

Follow these steps to perform your first DNA sequence analysis:

1

Upload a Sequence

Navigate to the DNA Analyzer page and upload your FASTA or FASTQ file by dragging it to the upload area or clicking to browse.

Don't have a sequence file? Click "Import Sample" to use a pre-loaded sample sequence.

2

Select Analysis Options

Choose which analysis features you want to run:

  • Basic Analysis: Sequence length, GC content, base composition
  • Codon Analysis: Start codons, stop codons, reading frames
  • Advanced Features: Coverage analysis, protein prediction, promoter detection
3

Run Analysis

Click the "Start Analysis" button to process your sequence. The analysis time depends on the sequence length and selected options.

4

Review Results

Examine the analysis results, which include visualizations, statistics, and detailed information about your sequence.

You can export the results in JSON, CSV, or text format for further processing or documentation.

Pro Tip

For large sequences (>1MB), consider using the local server installation for better performance and additional analysis options.

Sequence Analysis

DNAnalyzer provides comprehensive tools to analyze the basic properties of DNA sequences.

Base Composition Analysis

One of the fundamental analyses is determining the nucleotide composition of your DNA sequence:

  • Nucleotide Counts: Total count of A, T, G, C, and any other bases (N) in the sequence
  • Percentage Distribution: Proportion of each nucleotide type
  • Visualization: Interactive bar chart showing the distribution

GC Content Analysis

GC content is the percentage of nitrogenous bases in a DNA molecule that are either guanine (G) or cytosine (C):

GC Content Formula
GC Content (%) = (G + C) / (A + T + G + C) × 100

GC content is important because:

  • It affects DNA stability (higher GC content = more stable)
  • GC-rich regions often indicate regulatory elements and gene-dense areas
  • Different organisms have characteristic GC content ranges

Interpreting GC Content

  • Low GC (< 40%): Common in AT-rich genomes like certain bacteria
  • Medium GC (40-60%): Typical of many eukaryotic genomes, including humans
  • High GC (> 60%): Often found in gene-rich regions and certain prokaryotes

API Reference

DNAnalyzer provides a RESTful API when running in server mode, allowing programmatic access to all analysis features.

API Endpoints

The following endpoints are available when the server is running:

GET
/api/v1/status

Check server status and version information

Response

{
  "status": "running",
  "version": "1.0.0",
  "uptime": "12h 34m 56s"
}
POST
/api/v1/analyze

Analyze a DNA sequence file

Request

Form data with the following parameters:

  • dnaFile (required): The DNA sequence file to analyze
  • options: JSON string with analysis options

Response

{
  "sequence": {
    "length": 1234,
    "filename": "example.fa"
  },
  "basePairs": {
    "counts": { "A": 300, "T": 310, "G": 320, "C": 304 },
    "percentages": { "A": 24.3, "T": 25.1, "G": 25.9, "C": 24.7 },
    "gcContent": 50.6
  },
  "analysis": {
    // Additional analysis results based on options
  }
}
POST
/api/v1/base-pairs

Analyze base pair composition of a DNA sequence

Request

{
  "sequence": "ATGCATGCATGC"
}

Response

{
  "counts": { "A": 3, "T": 3, "G": 3, "C": 3 },
  "percentages": { "A": 25, "T": 25, "G": 25, "C": 25 },
  "gcContent": 50
}

Python Client Example

Here's a simple Python example to interact with the DNAnalyzer API:

Python
import requests

# API base URL
BASE_URL = "http://localhost:8080/api/v1"

# Check server status
response = requests.get(f"{BASE_URL}/status")
print("Server status:", response.json())

# Analyze a sequence
with open("sequence.fa", "rb") as f:
    files = {"dnaFile": f}
    options = {
        "sequence-length": True,
        "gc-content": True,
        "base-composition": True,
        "start-codons": True,
        "stop-codons": True
    }
    data = {"options": json.dumps(options)}
    response = requests.post(f"{BASE_URL}/analyze", files=files, data=data)
    
    # Print results
    result = response.json()
    print(f"Sequence length: {result['sequence']['length']}")
    print(f"GC content: {result['basePairs']['gcContent']}%")

Frequently Asked Questions

Is my DNA data secure when using DNAnalyzer?

Yes, DNAnalyzer is designed with privacy as a fundamental principle. All processing occurs locally on your device, and your DNA sequence data never leaves your system. In web mode, analysis runs directly in your browser, and in server mode, it runs only on your local machine. We don't collect, store, or transmit any of your sequence data.

What file formats does DNAnalyzer support?

DNAnalyzer supports the following file formats:

  • FASTA (.fa, .fasta)
  • FASTQ (.fastq)
  • Plain text (.txt) containing DNA sequences

The sequences should contain only A, T, G, C, and N (unknown) nucleotides. Other characters will be treated as unknown bases.

How large of a sequence can DNAnalyzer handle?

The size limitations depend on the mode of operation:

  • Web Interface: Up to around 5MB sequence files, depending on your browser and device memory
  • Local Server: Can handle much larger sequences, typically up to several hundred MB

For very large genomes (e.g., human genome), we recommend using the local server installation with a machine that has at least 8GB of RAM.

Can DNAnalyzer analyze my 23andMe or Ancestry DNA data?

Not directly in the current version. DNAnalyzer is designed to work with full DNA sequences rather than SNP genotype data from consumer genetics services. However, we're developing a feature to support genotype data analysis in an upcoming version.

If you have raw data from these services, you would need to convert it to a suitable format before using it with DNAnalyzer.

Is DNAnalyzer open source?

Yes, DNAnalyzer is completely open source under the MIT License. You can view, modify, and contribute to the code on our GitHub repository.

Community & Support

DNAnalyzer has an active community of users and developers. Here's how you can get involved and find support.

Getting Help

If you need assistance with DNAnalyzer, there are several resources available:

Discord Community

Join our Discord server for real-time assistance and discussions about DNAnalyzer.

Join Discord

GitHub Issues

Report bugs or request features through our GitHub issue tracker.

GitHub Issues

Documentation

Browse comprehensive guides and tutorials in our documentation.

View Docs

Contributing

DNAnalyzer is an open-source project, and we welcome contributions from the community. There are many ways to contribute:

  • Code Contributions: Help improve DNAnalyzer by fixing bugs or adding new features
  • Documentation: Improve our guides, tutorials, and API documentation
  • Testing: Test DNAnalyzer and report issues or suggest improvements
  • Translations: Help translate DNAnalyzer into different languages

To get started with contributing, please read our contribution guidelines.

Code of Conduct

We're committed to providing a welcoming and inclusive environment for all contributors. Please read and follow our Code of Conduct.

--------------------------------------------------