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!
×
--------------------------------------------------
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
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.
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.
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.
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.
Piyush Acharya
Founder & Lead Developer
Initiated DNAnalyzer to democratize genome analysis and make it
accessible while ensuring privacy.
@LimesKey
Co-Lead & ML Specialist
Leads the machine learning initiatives and computational biology aspects
of DNAnalyzer.
5
M+
DNA Sequences Analyzed
Global Impact
Users from 42 countries
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 = `
`;
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
Frame
Direction
Genes Found
`;
results.readingFrames.frames.forEach(frame => {
html += `
${frame.frame}
${frame.direction}
${frame.genes.length}
`;
});
html += `
`;
}
// Proteins table
if (options.includes('protein-prediction') && results.proteins && results.proteins.length > 0) {
html += `
Predicted Proteins
#
Length
Sequence (First 30 aa)
`;
results.proteins.forEach((protein, index) => {
const proteinSeq = protein.sequence || protein;
const displaySeq = proteinSeq.substring(0, 30) + (proteinSeq.length > 30 ? '...' : '');
html += `
${index + 1}
${protein.length || proteinSeq.length}
${displaySeq}
`;
});
html += `
`;
}
// Download options
html += `
Download Results
JSON
CSV
Text
`;
// 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 += `
Health Risks
`;
}
if (results.traits) {
tabsHtml += `
Traits
`;
}
if (results.ancestry) {
tabsHtml += `
Ancestry
`;
}
if (results.carrier) {
tabsHtml += `
Carrier Status
`;
}
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 => `
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.prediction}
${trait.confidence} confidence
SNPs: ${trait.snps.join(', ')}
`).join('')}
Behavioral Traits
${results.traits.behavioral.map(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 => `
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 Analysis
Genetic Testing
Batch Processing
Analysis Options
Start Analysis
Analyze DNA
Analyzing DNA sequence...
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.
Choose format to download your analysis results:
JSON
CSV
Text
Getting Started
To analyze DNA sequences, follow these steps:
Upload a DNA sequence file (.fa, .fasta, .fastq, .txt) by dragging it to the upload area or
clicking to browse.
Select the analysis options you want to run on your sequence.
Click "Start Analysis" to process your sequence.
View the results and download them if needed.
Analysis Options
DNAnalyzer offers several types of analysis:
Basic Analysis: Calculates sequence length, GC content percentage, and
nucleotide composition.
Codon Analysis: Identifies start codons (ATG), stop codons (TAA, TAG, TGA),
and reading frames.
Advanced Features: Performs coverage analysis, protein prediction, and
promoter sequence detection.
Need More Help?
Check out our Documentation for detailed guides or join our Discord community for support.
--------------------------------------------------
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
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
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 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
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
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 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
Copy
Clone the repository and navigate to the project directory
Setup Gradle
chmod +x ./gradlew
Copy
Make the Gradle wrapper executable (Unix/macOS systems)
Build the Project
./gradlew clean bootJar
Copy
Build the project (this may take a few minutes on first run)
Start the Server
java -jar build/libs/DNAnalyzer.jar
Copy
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
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:
- 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:
Visit DNAnalyzer Web App
Upload your DNA sequence files
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:
Download the latest DNAnalyzer
release for Windows
Extract the ZIP file to your preferred location
Ensure Java 17 or newer is installed on your system
Double-click on dnanalyzer.bat
to start the server
Access the interface at http://localhost:8080
in your browser
Download the latest DNAnalyzer
release for macOS
Extract the ZIP file to your preferred location
Ensure Java 17 or newer is installed on your system
Open Terminal and navigate to the extracted folder
Run chmod +x dnanalyzer.sh
to make the script executable
Execute ./dnanalyzer.sh
to start the server
Access the interface at http://localhost:8080
in your browser
Download the latest DNAnalyzer
release for Linux
Extract the archive to your preferred location
Ensure Java 17 or newer is installed on your system
Open Terminal and navigate to the extracted folder
Run chmod +x dnanalyzer.sh
to make the script executable
Execute ./dnanalyzer.sh
to start the server
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:
# 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 (%) = (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:
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 .
--------------------------------------------------