403Webshell
Server IP : 167.235.67.158  /  Your IP : 216.73.216.95
Web Server : Apache
System : Linux ubuntu-8gb-nbg1-1 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64
User : root ( 0)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/bc.the-word.com/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/bc.the-word.com/app.js
/**
 * 2026 Challenge Calendar App
 * A space-themed interactive calendar with 52 weekly challenges
 */

// ============================================
// PIXEL ART LAYOUT FOR "2026"
// ============================================
// Grid is 18 columns x 7 rows
// 1 = pixel on (part of the number), 0 = pixel off
// Total "on" pixels = 52 (one per week)

const PIXEL_ART_2026 = [
    // Row 0:  "2"      "0"      "2"      "6"
    //        (3px)   (2px)    (3px)    (3px) = 11
    [1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
    // Row 1: (1px)   (2px)    (1px)    (1px) = 5
    [0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
    // Row 2: (1px)   (2px)    (1px)    (1px) = 5
    [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
    // Row 3: (1px)   (2px)    (1px)    (3px) = 7
    [0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0],
    // Row 4: (1px)   (2px)    (1px)    (2px) = 6
    [1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
    // Row 5: (1px)   (2px)    (1px)    (2px) = 6
    [1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
    // Row 6: (4px)   (2px)    (4px)    (2px) = 12
    [1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0],
    // Digit totals: "2"=12, "0"=14, "2"=12, "6"=14 = 52 pixels!
];

// Define which columns belong to which digit (for sequential week numbering)
const DIGIT_COLUMNS = {
    0: [0, 1, 2, 3],    // First "2" 
    1: [5, 6, 7, 8],    // "0"
    2: [10, 11, 12, 13], // Second "2"
    3: [15, 16, 17, 18]  // "6"
};

// ============================================
// APP STATE
// ============================================
let challenges = [];
let openedChallenges = new Set();
let currentZoom = 0.8; // Start slightly zoomed out to fit "2026"
let panX = 0;
let panY = 0;
let isDragging = false;
let lastTouchDistance = 0;
let lastPanX = 0;
let lastPanY = 0;
let startX = 0;
let startY = 0;

// Audio context for sounds
let audioContext = null;

// ============================================
// INITIALIZATION
// ============================================
document.addEventListener('DOMContentLoaded', async () => {
    // Register Service Worker for PWA
    registerServiceWorker();

    // Load saved state
    loadOpenedChallenges();

    // Load challenges from JSON
    await loadChallenges();

    // Initialize starfield
    initStarfield();

    // Generate pixel grid
    generatePixelGrid();

    // Setup event listeners
    setupEventListeners();

    // Initialize audio context on first user interaction
    document.addEventListener('touchstart', initAudio, { once: true });
    document.addEventListener('click', initAudio, { once: true });
});

// ============================================
// SERVICE WORKER REGISTRATION
// ============================================
async function registerServiceWorker() {
    if ('serviceWorker' in navigator) {
        try {
            const registration = await navigator.serviceWorker.register('service-worker.js');
            console.log('Service Worker registered:', registration.scope);
        } catch (error) {
            console.error('Service Worker registration failed:', error);
        }
    }
}

// ============================================
// DATA LOADING
// ============================================
async function loadChallenges() {
    try {
        const response = await fetch('challenges.json');
        const data = await response.json();
        challenges = data.challenges;
    } catch (error) {
        console.error('Failed to load challenges:', error);
        // Generate placeholder challenges if file doesn't exist
        challenges = generatePlaceholderChallenges();
    }
}

function generatePlaceholderChallenges() {
    const placeholders = [];
    const startDate = new Date('2026-01-01');

    for (let i = 1; i <= 52; i++) {
        const unlockDate = new Date(startDate);
        unlockDate.setDate(startDate.getDate() + (i - 1) * 7);

        placeholders.push({
            week: i,
            unlockDate: unlockDate.toISOString().split('T')[0],
            title: `Week ${i}`,
            challenge: `Challenge for week ${i} - Coming soon!`
        });
    }

    return placeholders;
}

function loadOpenedChallenges() {
    try {
        const saved = localStorage.getItem('openedChallenges');
        if (saved) {
            openedChallenges = new Set(JSON.parse(saved));
        }
    } catch (error) {
        console.error('Failed to load opened challenges:', error);
    }
}

function saveOpenedChallenges() {
    try {
        localStorage.setItem('openedChallenges', JSON.stringify([...openedChallenges]));
    } catch (error) {
        console.error('Failed to save opened challenges:', error);
    }
}

// ============================================
// PIXEL GRID GENERATION
// ============================================
function generatePixelGrid() {
    const grid = document.getElementById('pixel-grid');
    const cols = PIXEL_ART_2026[0].length;
    const rows = PIXEL_ART_2026.length;

    // Set grid template
    grid.style.gridTemplateColumns = `repeat(${cols}, var(--tile-size))`;
    grid.style.gridTemplateRows = `repeat(${rows}, var(--tile-size))`;

    // Build week number map - assign weeks sequentially per digit
    const weekMap = buildWeekMap();

    for (let row = 0; row < rows; row++) {
        for (let col = 0; col < cols; col++) {
            if (PIXEL_ART_2026[row][col] === 1) {
                const weekNumber = weekMap[`${row},${col}`];
                const tile = createTile(weekNumber);
                grid.appendChild(tile);
            } else {
                // Empty space placeholder
                const spacer = document.createElement('div');
                spacer.className = 'pixel-spacer';
                spacer.style.width = 'var(--tile-size)';
                spacer.style.height = 'var(--tile-size)';
                grid.appendChild(spacer);
            }
        }
    }

    // Apply initial zoom
    updateTransform();
}

// Build a map of (row,col) -> week number, numbering each digit sequentially
function buildWeekMap() {
    const weekMap = {};
    let weekNumber = 1;
    const rows = PIXEL_ART_2026.length;

    // Process each digit in order (0=first "2", 1="0", 2=second "2", 3="6")
    for (let digit = 0; digit <= 3; digit++) {
        const digitCols = DIGIT_COLUMNS[digit];

        // Go through each row, but only for this digit's columns
        for (let row = 0; row < rows; row++) {
            for (const col of digitCols) {
                if (PIXEL_ART_2026[row][col] === 1) {
                    weekMap[`${row},${col}`] = weekNumber;
                    weekNumber++;
                }
            }
        }
    }

    return weekMap;
}

function createTile(weekNumber) {
    const tile = document.createElement('div');
    tile.className = 'pixel-tile';
    tile.dataset.week = weekNumber;

    // Add accessibility attributes
    tile.setAttribute('role', 'button');
    tile.setAttribute('tabindex', '0');
    tile.setAttribute('aria-label', `Week ${weekNumber} challenge`);

    const challenge = challenges.find(c => c.week === weekNumber);
    const unlockDate = challenge ? new Date(challenge.unlockDate) : getDefaultUnlockDate(weekNumber);
    const isUnlocked = isWeekUnlocked(unlockDate);
    const isOpened = openedChallenges.has(weekNumber);

    // Set state class
    if (isOpened) {
        tile.classList.add('opened');
        const weekNum = document.createElement('span');
        weekNum.className = 'week-num';
        weekNum.textContent = weekNumber;
        tile.appendChild(weekNum);
    } else if (isUnlocked) {
        tile.classList.add('unlocked');
        const weekNum = document.createElement('span');
        weekNum.className = 'week-num';
        weekNum.textContent = weekNumber;
        tile.appendChild(weekNum);
    } else {
        tile.classList.add('locked');
    }

    // Store unlock date for tooltip
    tile.dataset.unlockDate = unlockDate.toISOString();

    // Click handler
    tile.addEventListener('click', () => handleTileClick(weekNumber));

    // Keyboard handler for accessibility
    tile.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            handleTileClick(weekNumber);
        }
    });

    return tile;
}

function getDefaultUnlockDate(weekNumber) {
    const startDate = new Date('2026-01-01');
    startDate.setDate(startDate.getDate() + (weekNumber - 1) * 7);
    return startDate;
}

function isWeekUnlocked(unlockDate) {
    // Check for test mode via URL parameter ?test=true
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('test') === 'true') {
        return true; // All weeks unlocked in test mode
    }

    const now = new Date();
    return now >= unlockDate;
}

// ============================================
// TILE CLICK HANDLING
// ============================================
function handleTileClick(weekNumber) {
    const challenge = challenges.find(c => c.week === weekNumber);
    const unlockDate = challenge ? new Date(challenge.unlockDate) : getDefaultUnlockDate(weekNumber);

    if (!isWeekUnlocked(unlockDate)) {
        showLockedToast(unlockDate);
        return;
    }

    // Mark as opened
    openedChallenges.add(weekNumber);
    saveOpenedChallenges();

    // Update tile appearance
    const tile = document.querySelector(`[data-week="${weekNumber}"]`);
    if (tile) {
        tile.classList.remove('unlocked');
        tile.classList.add('opened');
    }

    // Show cube animation and challenge
    showChallenge(weekNumber, challenge);
}

function showLockedToast(unlockDate) {
    const toast = document.getElementById('locked-toast');
    const message = document.getElementById('locked-message');

    const options = { month: 'long', day: 'numeric' };
    const dateStr = unlockDate.toLocaleDateString('en-US', options);
    message.textContent = `Opens on ${dateStr}`;

    toast.classList.remove('hidden');

    // Play locked sound
    playSound('locked');

    // Hide after 2 seconds
    setTimeout(() => {
        toast.classList.add('hidden');
    }, 2000);
}

// ============================================
// CHALLENGE MODAL & CUBE ANIMATION
// ============================================
function showChallenge(weekNumber, challenge) {
    const overlay = document.getElementById('modal-overlay');
    const cube = document.getElementById('cube');
    const cubeContainer = document.getElementById('cube-container');
    const challengeContent = document.getElementById('challenge-content');
    const cubeWeek = document.getElementById('cube-week');

    // Reset state
    cube.className = '';
    challengeContent.classList.add('hidden');
    cubeContainer.style.display = 'block';

    // Set week number on cube
    cubeWeek.textContent = weekNumber;

    // Show overlay
    overlay.classList.remove('hidden');

    // Play whoosh sound
    playSound('whoosh');

    // Start cube spinning animation
    setTimeout(() => {
        cube.classList.add('spinning');
    }, 100);

    // After spin, open cube and show challenge
    setTimeout(() => {
        cube.classList.remove('spinning');
        cube.classList.add('opening');
        playSound('unlock');

        // Show confetti
        launchConfetti();
    }, 1600);

    // Show challenge content
    setTimeout(() => {
        cubeContainer.style.display = 'none';
        challengeContent.classList.remove('hidden');

        // Populate challenge content
        document.getElementById('challenge-week-num').textContent = weekNumber;

        const unlockDate = challenge ? new Date(challenge.unlockDate) : getDefaultUnlockDate(weekNumber);
        const options = { month: 'long', day: 'numeric', year: 'numeric' };
        document.getElementById('challenge-date').textContent = unlockDate.toLocaleDateString('en-US', options);

        document.getElementById('challenge-text').textContent =
            challenge ? challenge.challenge : `Challenge ${weekNumber} - Coming soon!`;
    }, 2200);
}

function closeChallenge() {
    const overlay = document.getElementById('modal-overlay');
    overlay.classList.add('hidden');
}

// ============================================
// STARFIELD BACKGROUND
// ============================================
function initStarfield() {
    const canvas = document.getElementById('starfield');
    const ctx = canvas.getContext('2d');

    // Pre-create the nebula gradient
    let nebulaGradient = null;
    function createNebulaGradient() {
        nebulaGradient = ctx.createRadialGradient(
            canvas.width * 0.3, canvas.height * 0.4, 0,
            canvas.width * 0.3, canvas.height * 0.4, canvas.width * 0.5
        );
        nebulaGradient.addColorStop(0, 'rgba(124, 58, 237, 0.1)');
        nebulaGradient.addColorStop(0.5, 'rgba(59, 130, 246, 0.05)');
        nebulaGradient.addColorStop(1, 'transparent');
    }

    // Set canvas size
    function resizeCanvas() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        createNebulaGradient();
    }
    resizeCanvas();
    window.addEventListener('resize', resizeCanvas);

    // Create stars with twinkling properties - reduced count for performance
    const stars = [];
    const numStars = 100; // Reduced from 200

    // Pre-calculate colors array
    const starColors = ['#ffffff', '#e0e7ff', '#c4b5fd', '#a5b4fc', '#93c5fd'];

    for (let i = 0; i < numStars; i++) {
        stars.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            radius: Math.random() * 1.5 + 0.5,
            baseAlpha: Math.random() * 0.5 + 0.3,
            alpha: Math.random(),
            twinkleSpeed: Math.random() * 0.02 + 0.005,
            twinklePhase: Math.random() * Math.PI * 2,
            twinkleIntensity: Math.random() * 0.4 + 0.2,
            color: starColors[Math.floor(Math.random() * starColors.length)]
        });
    }

    // Throttle animation to ~30fps for better performance
    let lastFrameTime = 0;
    const frameInterval = 1000 / 30;

    // Animation loop
    function animate(currentTime) {
        requestAnimationFrame(animate);

        // Throttle frame rate
        if (currentTime - lastFrameTime < frameInterval) return;
        lastFrameTime = currentTime;

        ctx.fillStyle = '#0a0a1a';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Draw nebula effect (using cached gradient)
        ctx.fillStyle = nebulaGradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Draw stars with simple twinkling
        for (let i = 0; i < stars.length; i++) {
            const star = stars[i];

            // Simple sine-wave twinkling
            star.twinklePhase += star.twinkleSpeed;
            star.alpha = star.baseAlpha + Math.sin(star.twinklePhase) * star.twinkleIntensity;

            ctx.globalAlpha = star.alpha;
            ctx.fillStyle = star.color;
            ctx.beginPath();
            ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);
            ctx.fill();
        }

        ctx.globalAlpha = 1;
    }

    requestAnimationFrame(animate);
}

// ============================================
// CONFETTI EFFECT
// ============================================
function launchConfetti() {
    const canvas = document.getElementById('confetti-canvas');
    const ctx = canvas.getContext('2d');

    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const particles = [];
    const colors = ['#8b5cf6', '#ec4899', '#3b82f6', '#fbbf24', '#10b981', '#06b6d4'];
    const numParticles = 80; // Fewer particles for quick burst

    // Create particles - burst outward from center
    for (let i = 0; i < numParticles; i++) {
        const angle = (Math.PI * 2 * i) / numParticles;
        const speed = Math.random() * 15 + 10;
        particles.push({
            x: canvas.width / 2,
            y: canvas.height / 2,
            vx: Math.cos(angle) * speed + (Math.random() - 0.5) * 5,
            vy: Math.sin(angle) * speed - 5,
            color: colors[Math.floor(Math.random() * colors.length)],
            size: Math.random() * 8 + 4,
            rotation: Math.random() * 360,
            rotationSpeed: (Math.random() - 0.5) * 15,
            gravity: 0.5,
            alpha: 1,
            shape: Math.random() > 0.5 ? 'square' : 'circle'
        });
    }

    function animateConfetti() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        let activeParticles = 0;

        particles.forEach(p => {
            if (p.alpha <= 0) return;

            activeParticles++;

            // Update position
            p.x += p.vx;
            p.y += p.vy;
            p.vy += p.gravity;
            p.rotation += p.rotationSpeed;
            p.alpha -= 0.04; // Much faster fade for quick burst
            p.vx *= 0.98;

            // Draw
            ctx.save();
            ctx.translate(p.x, p.y);
            ctx.rotate(p.rotation * Math.PI / 180);
            ctx.globalAlpha = p.alpha;
            ctx.fillStyle = p.color;

            if (p.shape === 'square') {
                ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);
            } else {
                ctx.beginPath();
                ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2);
                ctx.fill();
            }

            ctx.restore();
        });

        if (activeParticles > 0) {
            requestAnimationFrame(animateConfetti);
        } else {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
    }

    animateConfetti();
}

// ============================================
// SOUND EFFECTS
// ============================================
function initAudio() {
    if (!audioContext) {
        audioContext = new (window.AudioContext || window.webkitAudioContext)();
    }
}

function playSound(type) {
    if (!audioContext) return;

    const oscillator = audioContext.createOscillator();
    const gainNode = audioContext.createGain();

    oscillator.connect(gainNode);
    gainNode.connect(audioContext.destination);

    switch (type) {
        case 'whoosh':
            oscillator.type = 'sine';
            oscillator.frequency.setValueAtTime(200, audioContext.currentTime);
            oscillator.frequency.exponentialRampToValueAtTime(800, audioContext.currentTime + 0.3);
            gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
            oscillator.start();
            oscillator.stop(audioContext.currentTime + 0.3);
            break;

        case 'unlock':
            // Play a pleasant chime
            const frequencies = [523.25, 659.25, 783.99]; // C5, E5, G5
            frequencies.forEach((freq, i) => {
                const osc = audioContext.createOscillator();
                const gain = audioContext.createGain();
                osc.connect(gain);
                gain.connect(audioContext.destination);
                osc.type = 'sine';
                osc.frequency.value = freq;
                gain.gain.setValueAtTime(0.2, audioContext.currentTime + i * 0.1);
                gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + i * 0.1 + 0.5);
                osc.start(audioContext.currentTime + i * 0.1);
                osc.stop(audioContext.currentTime + i * 0.1 + 0.5);
            });
            break;

        case 'locked':
            oscillator.type = 'square';
            oscillator.frequency.setValueAtTime(150, audioContext.currentTime);
            oscillator.frequency.setValueAtTime(100, audioContext.currentTime + 0.1);
            gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
            oscillator.start();
            oscillator.stop(audioContext.currentTime + 0.2);
            break;
    }
}

// ============================================
// ZOOM & PAN FUNCTIONALITY
// ============================================
function setupEventListeners() {
    const viewport = document.getElementById('viewport');
    const grid = document.getElementById('pixel-grid');

    // Touch events for pinch zoom and pan
    viewport.addEventListener('touchstart', handleTouchStart, { passive: false });
    viewport.addEventListener('touchmove', handleTouchMove, { passive: false });
    viewport.addEventListener('touchend', handleTouchEnd);

    // Mouse events for desktop
    viewport.addEventListener('mousedown', handleMouseDown);
    viewport.addEventListener('mousemove', handleMouseMove);
    viewport.addEventListener('mouseup', handleMouseUp);
    viewport.addEventListener('mouseleave', handleMouseUp);
    viewport.addEventListener('wheel', handleWheel, { passive: false });

    // Close challenge button
    document.getElementById('close-challenge').addEventListener('click', closeChallenge);

    // Close modal on overlay click
    document.getElementById('modal-overlay').addEventListener('click', (e) => {
        if (e.target.id === 'modal-overlay') {
            closeChallenge();
        }
    });
}

function handleTouchStart(e) {
    if (e.touches.length === 2) {
        // Pinch zoom start
        e.preventDefault();
        lastTouchDistance = getTouchDistance(e.touches);
    } else if (e.touches.length === 1) {
        // Pan start
        isDragging = true;
        startX = e.touches[0].clientX - panX;
        startY = e.touches[0].clientY - panY;
    }
}

function handleTouchMove(e) {
    if (e.touches.length === 2) {
        // Pinch zoom
        e.preventDefault();
        const currentDistance = getTouchDistance(e.touches);
        const delta = currentDistance - lastTouchDistance;

        currentZoom = Math.max(0.5, Math.min(3, currentZoom + delta * 0.005));
        lastTouchDistance = currentDistance;

        updateTransform();
    } else if (e.touches.length === 1 && isDragging) {
        // Pan
        panX = e.touches[0].clientX - startX;
        panY = e.touches[0].clientY - startY;
        updateTransform();
    }
}

function handleTouchEnd() {
    isDragging = false;
    lastTouchDistance = 0;
}

function getTouchDistance(touches) {
    const dx = touches[0].clientX - touches[1].clientX;
    const dy = touches[0].clientY - touches[1].clientY;
    return Math.sqrt(dx * dx + dy * dy);
}

function handleMouseDown(e) {
    if (e.target.classList.contains('pixel-tile')) return;
    isDragging = true;
    startX = e.clientX - panX;
    startY = e.clientY - panY;
}

function handleMouseMove(e) {
    if (!isDragging) return;
    panX = e.clientX - startX;
    panY = e.clientY - startY;
    updateTransform();
}

function handleMouseUp() {
    isDragging = false;
}

function handleWheel(e) {
    e.preventDefault();
    const delta = e.deltaY > 0 ? -0.1 : 0.1;
    currentZoom = Math.max(0.5, Math.min(3, currentZoom + delta));
    updateTransform();
}

function updateTransform() {
    const grid = document.getElementById('pixel-grid');
    grid.style.transform = `translate(${panX}px, ${panY}px) scale(${currentZoom})`;
}

// ============================================
// UTILITY FUNCTIONS
// ============================================
function formatDate(date) {
    const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
    return date.toLocaleDateString('en-US', options);
}


Youez - 2016 - github.com/yon3zu
LinuXploit