Spaces:
Running
Running
| // CyberVanguard - Main JavaScript | |
| // Theme Toggle | |
| const initTheme = () => { | |
| const theme = localStorage.getItem('theme') || 'dark'; | |
| document.documentElement.setAttribute('data-theme', theme); | |
| return theme; | |
| }; | |
| const toggleTheme = () => { | |
| const current = document.documentElement.getAttribute('data-theme'); | |
| const next = current === 'dark' ? 'light' : 'dark'; | |
| document.documentElement.setAttribute('data-theme', next); | |
| localStorage.setItem('theme', next); | |
| }; | |
| // Matrix Rain Effect | |
| class MatrixRain { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext('2d'); | |
| this.chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$#@%&*'; | |
| this.drops = []; | |
| this.fontSize = 14; | |
| this.init(); | |
| } | |
| init() { | |
| this.resize(); | |
| window.addEventListener('resize', () => this.resize()); | |
| const columns = Math.floor(this.canvas.width / this.fontSize); | |
| this.drops = Array(columns).fill(1); | |
| this.animate(); | |
| } | |
| resize() { | |
| this.canvas.width = this.canvas.scrollWidth; | |
| this.canvas.height = this.canvas.scrollHeight; | |
| } | |
| animate() { | |
| this.ctx.fillStyle = 'rgba(2, 6, 23, 0.05)'; | |
| this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
| this.ctx.fillStyle = '#22c55e'; | |
| this.ctx.font = `${this.fontSize}px JetBrains Mono`; | |
| for (let i = 0; i < this.drops.length; i++) { | |
| const char = this.chars[Math.floor(Math.random() * this.chars.length)]; | |
| const x = i * this.fontSize; | |
| const y = this.drops[i] * this.fontSize; | |
| // Randomize colors for visual interest | |
| const hue = Math.random() > 0.95 ? 340 : 142; // Green or pink | |
| this.ctx.fillStyle = `hsl(${hue}, 70%, ${Math.random() > 0.5 ? 60 : 40}%)`; | |
| this.ctx.fillText(char, x, y); | |
| if (y > this.canvas.height && Math.random() > 0.975) { | |
| this.drops[i] = 0; | |
| } | |
| this.drops[i]++; | |
| } | |
| this.animationId = requestAnimationFrame(() => this.animate()); | |
| } | |
| destroy() { | |
| cancelAnimationFrame(this.animationId); | |
| } | |
| } | |
| // Counter Animation | |
| const animateCounters = () => { | |
| const counters = document.querySelectorAll('[data-count]'); | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const counter = entry.target; | |
| const target = parseInt(counter.dataset.count); | |
| const duration = 2000; | |
| const step = target / (duration / 16); | |
| let current = 0; | |
| const updateCounter = () => { | |
| current += step; | |
| if (current < target) { | |
| counter.textContent = Math.floor(current).toLocaleString(); | |
| requestAnimationFrame(updateCounter); | |
| } else { | |
| counter.textContent = target.toLocaleString() + (target < 100 ? '%' : '+'); | |
| } | |
| }; | |
| updateCounter(); | |
| observer.unobserve(counter); | |
| } | |
| }); | |
| }, { threshold: 0.5 }); | |
| counters.forEach(counter => observer.observe(counter)); | |
| }; | |
| // Scroll Animations | |
| const initScrollAnimations = () => { | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-slide-up'); | |
| observer.unobserve(entry.target); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('.hover-lift').forEach(el => { | |
| el.classList.add('opacity-0', 'translate-y-4'); | |
| observer.observe(el); | |
| }); | |
| }; | |
| // Terminal Interaction | |
| class TerminalController { | |
| constructor(element) { | |
| this.element = element; | |
| this.history = []; | |
| this.historyIndex = -1; | |
| this.currentLine = ''; | |
| this.isTyping = false; | |
| } | |
| async typeLine(text, type = 'command') { | |
| const line = document.createElement('div'); | |
| line.className = 'terminal-line'; | |
| if (type === 'command') { | |
| line.innerHTML = `<span class="terminal-prompt">student@cybervanguard:~$</span> `; | |
| } | |
| this.element.appendChild(line); | |
| for (let i = 0; i < text.length; i++) { | |
| await this.delay(30); | |
| if (type === 'command') { | |
| line.innerHTML += `<span class="terminal-command">${text[i]}</span>`; | |
| } else { | |
| line.innerHTML += `<span class="terminal-output">${text[i]}</span>`; | |
| } | |
| } | |
| this.element.scrollTop = this.element.scrollHeight; | |
| } | |
| delay(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| async runDemo() { | |
| const commands = [ | |
| { cmd: 'whoami', out: 'student' }, | |
| { cmd: 'nmap -sV 192.168.1.0/24', out: 'Starting Nmap 7.94...\nScanning 256 hosts...\nFound: 192.168.1.1 (router)\nFound: 192.168.1.100 (web-server)\nPorts: 22/ssh, 80/http, 443/https open' }, | |
| { cmd: 'enum4linux -a 192.168.1.100', out: 'Starting enum4linux...\n[+] Got domain/workgroup name: CYBERLAB\n[+] OS: Windows Server 2019\n[+] Users: admin, backup_svc, guest' }, | |
| { cmd: 'python3 exploit.py --target 192.168.1.100', out: '[*] Checking target vulnerability...\n[+] Target is vulnerable to CVE-2023-1234\n[*] Sending payload...\n[+] Got shell! spawn /bin/bash\nstudent@web-server:~$' } | |
| ]; | |
| for (const { cmd, out } of commands) { | |
| await this.typeLine(cmd, 'command'); | |
| await this.delay(500); | |
| await this.typeLine(out, 'output'); | |
| await this.delay(800); | |
| } | |
| // Add blinking cursor | |
| const cursor = document.createElement('span'); | |
| cursor.className = 'terminal-cursor'; | |
| this.element.lastElementChild?.appendChild(cursor); | |
| } | |
| } | |
| // Navigation | |
| const initNavigation = () => { | |
| let lastScroll = 0; | |
| const navbar = document.querySelector('cyber-navbar'); | |
| window.addEventListener('scroll', () => { | |
| const current = window.pageYOffset; | |
| if (current > 100) { | |
| navbar?.classList.add('scrolled'); | |
| } else { | |
| navbar?.classList.remove('scrolled'); | |
| } | |
| lastScroll = current; | |
| }); | |
| }; | |
| // Mobile Menu | |
| const initMobileMenu = () => { | |
| const toggle = document.querySelector('[data-menu-toggle]'); | |
| const menu = document.querySelector('[data-mobile-menu]'); | |
| toggle?.addEventListener('click', () => { | |
| menu?.classList.toggle('hidden'); | |
| toggle.setAttribute('aria-expanded', | |
| menu?.classList.contains('hidden') ? 'false' : 'true' | |
| ); | |
| }); | |
| }; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Initialize theme | |
| initTheme(); | |
| // Initialize Matrix effect if canvas exists | |
| const matrixCanvas = document.getElementById('matrix-canvas'); | |
| if (matrixCanvas) { | |
| new MatrixRain(matrixCanvas); | |
| } | |
| // Initialize counters | |
| animateCounters(); | |
| // Initialize scroll animations | |
| initScrollAnimations(); | |
| // Initialize navigation | |
| initNavigation(); | |
| // Initialize mobile menu | |
| initMobileMenu(); | |
| // Initialize terminal if exists | |
| const terminalBody = document.querySelector('[data-terminal-body]'); | |
| if (terminalBody) { | |
| const terminal = new TerminalController(terminalBody); | |
| setTimeout(() => terminal.runDemo(), 1000); | |
| } | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| target.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
| } | |
| }); | |
| }); | |
| }); | |
| // Export for use in components | |
| window.CyberVanguard = { | |
| MatrixRain, | |
| TerminalController, | |
| toggleTheme, | |
| initTheme | |
| }; |