1033 lines
34 KiB
HTML
1033 lines
34 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Cosmic Calendar - Space Exploration Calendar System</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Arial', sans-serif;
|
||
background: #000;
|
||
color: #fff;
|
||
overflow-x: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
/* Cosmic Background */
|
||
.cosmic-background {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: radial-gradient(ellipse at center, #0a0a2a 0%, #000 100%);
|
||
z-index: -2;
|
||
}
|
||
|
||
.stars {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: -1;
|
||
}
|
||
|
||
.star {
|
||
position: absolute;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
animation: twinkle 3s infinite;
|
||
}
|
||
|
||
@keyframes twinkle {
|
||
0%, 100% { opacity: 0.3; transform: scale(1); }
|
||
50% { opacity: 1; transform: scale(1.2); }
|
||
}
|
||
|
||
/* Nebula Effects */
|
||
.nebula {
|
||
position: fixed;
|
||
width: 300px;
|
||
height: 300px;
|
||
background: radial-gradient(circle, rgba(138, 43, 226, 0.2), transparent 70%);
|
||
filter: blur(40px);
|
||
animation: drift 30s infinite;
|
||
z-index: -1;
|
||
}
|
||
|
||
.nebula:nth-child(2) {
|
||
background: radial-gradient(circle, rgba(30, 144, 255, 0.2), transparent 70%);
|
||
animation-duration: 40s;
|
||
animation-direction: reverse;
|
||
}
|
||
|
||
@keyframes drift {
|
||
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||
33% { transform: translate(100px, -50px) rotate(120deg); }
|
||
66% { transform: translate(-50px, 100px) rotate(240deg); }
|
||
}
|
||
|
||
/* Header */
|
||
.header {
|
||
text-align: center;
|
||
padding: 30px;
|
||
background: linear-gradient(to bottom, rgba(0, 0, 40, 0.8), transparent);
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 3em;
|
||
background: linear-gradient(90deg, #4a90e2, #9b59b6, #e74c3c);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
text-shadow: 0 0 30px rgba(74, 144, 226, 0.5);
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.header p {
|
||
color: #8a9bb3;
|
||
font-size: 1.2em;
|
||
}
|
||
|
||
/* Main Container */
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
display: grid;
|
||
grid-template-columns: 300px 1fr 300px;
|
||
gap: 30px;
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
/* Left Panel - Time Zones */
|
||
.time-zones {
|
||
background: rgba(0, 0, 40, 0.6);
|
||
border: 1px solid rgba(74, 144, 226, 0.3);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.time-zones h2 {
|
||
color: #4a90e2;
|
||
margin-bottom: 20px;
|
||
font-size: 1.3em;
|
||
}
|
||
|
||
.planet-time {
|
||
margin-bottom: 15px;
|
||
padding: 15px;
|
||
background: rgba(74, 144, 226, 0.1);
|
||
border-radius: 10px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.planet-time:hover {
|
||
background: rgba(74, 144, 226, 0.2);
|
||
transform: translateX(5px);
|
||
}
|
||
|
||
.planet-time::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 40px;
|
||
height: 40px;
|
||
background: radial-gradient(circle, var(--planet-color), transparent);
|
||
border-radius: 50%;
|
||
top: 50%;
|
||
left: -20px;
|
||
transform: translateY(-50%);
|
||
animation: rotate 20s linear infinite;
|
||
}
|
||
|
||
@keyframes rotate {
|
||
0% { transform: translateY(-50%) rotate(0deg); }
|
||
100% { transform: translateY(-50%) rotate(360deg); }
|
||
}
|
||
|
||
.planet-name {
|
||
font-weight: bold;
|
||
color: #fff;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.planet-time-display {
|
||
color: #8a9bb3;
|
||
font-family: monospace;
|
||
font-size: 1.1em;
|
||
}
|
||
|
||
/* Calendar Grid */
|
||
.calendar-container {
|
||
background: rgba(0, 0, 40, 0.6);
|
||
border: 1px solid rgba(74, 144, 226, 0.3);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.calendar-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.month-year {
|
||
font-size: 1.8em;
|
||
color: #4a90e2;
|
||
text-align: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.nav-button {
|
||
background: rgba(74, 144, 226, 0.2);
|
||
border: 1px solid rgba(74, 144, 226, 0.5);
|
||
color: #fff;
|
||
padding: 10px 20px;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
font-size: 1.2em;
|
||
}
|
||
|
||
.nav-button:hover {
|
||
background: rgba(74, 144, 226, 0.4);
|
||
transform: scale(1.05);
|
||
box-shadow: 0 0 15px rgba(74, 144, 226, 0.5);
|
||
}
|
||
|
||
.calendar-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(7, 1fr);
|
||
gap: 10px;
|
||
position: relative;
|
||
}
|
||
|
||
.day-header {
|
||
text-align: center;
|
||
color: #8a9bb3;
|
||
padding: 10px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.calendar-day {
|
||
aspect-ratio: 1;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(74, 144, 226, 0.2);
|
||
border-radius: 10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.calendar-day:hover {
|
||
background: rgba(74, 144, 226, 0.2);
|
||
border-color: rgba(74, 144, 226, 0.5);
|
||
transform: scale(1.05);
|
||
z-index: 1;
|
||
}
|
||
|
||
.calendar-day.other-month {
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.calendar-day.today {
|
||
background: radial-gradient(circle, rgba(255, 215, 0, 0.3), transparent);
|
||
border-color: rgba(255, 215, 0, 0.5);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); }
|
||
50% { box-shadow: 0 0 25px rgba(255, 215, 0, 0.8); }
|
||
}
|
||
|
||
.day-number {
|
||
font-size: 1.2em;
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.event-indicator {
|
||
width: 6px;
|
||
height: 6px;
|
||
background: var(--event-color);
|
||
border-radius: 50%;
|
||
margin: 2px;
|
||
animation: orbit 3s linear infinite;
|
||
}
|
||
|
||
@keyframes orbit {
|
||
0% { transform: rotate(0deg) translateX(10px) rotate(0deg); }
|
||
100% { transform: rotate(360deg) translateX(10px) rotate(-360deg); }
|
||
}
|
||
|
||
/* Right Panel - Events */
|
||
.events-panel {
|
||
background: rgba(0, 0, 40, 0.6);
|
||
border: 1px solid rgba(74, 144, 226, 0.3);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.events-panel h2 {
|
||
color: #4a90e2;
|
||
margin-bottom: 20px;
|
||
font-size: 1.3em;
|
||
}
|
||
|
||
.add-event-btn {
|
||
width: 100%;
|
||
background: linear-gradient(45deg, #4a90e2, #9b59b6);
|
||
border: none;
|
||
color: #fff;
|
||
padding: 15px;
|
||
border-radius: 10px;
|
||
cursor: pointer;
|
||
font-size: 1.1em;
|
||
transition: all 0.3s;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.add-event-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 20px rgba(74, 144, 226, 0.3);
|
||
}
|
||
|
||
.event-item {
|
||
background: rgba(74, 144, 226, 0.1);
|
||
border: 1px solid rgba(74, 144, 226, 0.3);
|
||
border-radius: 10px;
|
||
padding: 15px;
|
||
margin-bottom: 15px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.event-item:hover {
|
||
background: rgba(74, 144, 226, 0.2);
|
||
transform: translateX(-5px);
|
||
}
|
||
|
||
.event-item::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 20px;
|
||
height: 20px;
|
||
background: radial-gradient(circle, var(--event-color), transparent);
|
||
border-radius: 50%;
|
||
top: 50%;
|
||
right: -10px;
|
||
transform: translateY(-50%);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
.event-title {
|
||
font-weight: bold;
|
||
color: #fff;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.event-time {
|
||
color: #8a9bb3;
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
/* Event Modal */
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
z-index: 1000;
|
||
backdrop-filter: blur(5px);
|
||
}
|
||
|
||
.modal-content {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
background: linear-gradient(135deg, rgba(0, 0, 40, 0.95), rgba(74, 144, 226, 0.1));
|
||
border: 1px solid rgba(74, 144, 226, 0.5);
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
width: 90%;
|
||
max-width: 500px;
|
||
box-shadow: 0 0 50px rgba(74, 144, 226, 0.5);
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
color: #4a90e2;
|
||
font-size: 1.5em;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
color: #fff;
|
||
font-size: 1.5em;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
color: #e74c3c;
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
color: #8a9bb3;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(74, 144, 226, 0.3);
|
||
color: #fff;
|
||
padding: 10px;
|
||
border-radius: 5px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus,
|
||
.form-group textarea:focus {
|
||
outline: none;
|
||
border-color: rgba(74, 144, 226, 0.8);
|
||
box-shadow: 0 0 10px rgba(74, 144, 226, 0.3);
|
||
}
|
||
|
||
.color-picker {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.color-option {
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
border: 2px solid transparent;
|
||
}
|
||
|
||
.color-option:hover {
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
.color-option.selected {
|
||
border-color: #fff;
|
||
box-shadow: 0 0 10px currentColor;
|
||
}
|
||
|
||
.save-btn {
|
||
background: linear-gradient(45deg, #4a90e2, #9b59b6);
|
||
border: none;
|
||
color: #fff;
|
||
padding: 12px 30px;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 1.1em;
|
||
transition: all 0.3s;
|
||
width: 100%;
|
||
}
|
||
|
||
.save-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 20px rgba(74, 144, 226, 0.3);
|
||
}
|
||
|
||
/* Satellite Signals (Reminders) */
|
||
.satellite-signal {
|
||
position: fixed;
|
||
width: 100px;
|
||
height: 100px;
|
||
border: 2px solid rgba(255, 215, 0, 0.5);
|
||
border-radius: 50%;
|
||
animation: signal-pulse 3s infinite;
|
||
pointer-events: none;
|
||
z-index: 100;
|
||
}
|
||
|
||
@keyframes signal-pulse {
|
||
0% {
|
||
transform: scale(0.5);
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
transform: scale(2);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* Comet Trail (Recurring Events) */
|
||
.comet {
|
||
position: fixed;
|
||
width: 10px;
|
||
height: 10px;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 10px #fff;
|
||
z-index: 5;
|
||
}
|
||
|
||
.comet-trail {
|
||
position: absolute;
|
||
width: 100px;
|
||
height: 2px;
|
||
background: linear-gradient(to left, transparent, rgba(255, 255, 255, 0.8));
|
||
top: 50%;
|
||
right: 10px;
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
/* Orbital Paths */
|
||
.orbital-path {
|
||
position: absolute;
|
||
border: 1px dashed rgba(74, 144, 226, 0.3);
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
animation: rotate 30s linear infinite;
|
||
}
|
||
|
||
/* Constellation Connections */
|
||
.constellation-line {
|
||
position: absolute;
|
||
height: 1px;
|
||
background: rgba(74, 144, 226, 0.3);
|
||
transform-origin: left center;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 1200px) {
|
||
.container {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.time-zones,
|
||
.events-panel {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="cosmic-background"></div>
|
||
<div class="stars" id="starsContainer"></div>
|
||
<div class="nebula" style="top: 20%; left: 10%;"></div>
|
||
<div class="nebula" style="bottom: 20%; right: 10%;"></div>
|
||
|
||
<header class="header">
|
||
<h1>Cosmic Calendar</h1>
|
||
<p>Navigate Time Through the Stars</p>
|
||
</header>
|
||
|
||
<div class="container">
|
||
<!-- Time Zones Panel -->
|
||
<div class="time-zones">
|
||
<h2>Planetary Time Zones</h2>
|
||
<div id="timeZones"></div>
|
||
</div>
|
||
|
||
<!-- Calendar -->
|
||
<div class="calendar-container">
|
||
<div class="calendar-header">
|
||
<button class="nav-button" onclick="changeMonth(-1)">◀</button>
|
||
<div class="month-year" id="monthYear"></div>
|
||
<button class="nav-button" onclick="changeMonth(1)">▶</button>
|
||
</div>
|
||
<div class="calendar-grid" id="calendarGrid"></div>
|
||
</div>
|
||
|
||
<!-- Events Panel -->
|
||
<div class="events-panel">
|
||
<h2>Celestial Events</h2>
|
||
<button class="add-event-btn" onclick="openModal()">Launch New Event</button>
|
||
<div id="eventsList"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Event Modal -->
|
||
<div class="modal" id="eventModal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Create Celestial Event</h3>
|
||
<button class="close-btn" onclick="closeModal()">×</button>
|
||
</div>
|
||
<form id="eventForm">
|
||
<div class="form-group">
|
||
<label>Event Name</label>
|
||
<input type="text" id="eventName" placeholder="Enter celestial event name" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Date</label>
|
||
<input type="date" id="eventDate" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Time</label>
|
||
<input type="time" id="eventTime" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Description</label>
|
||
<textarea id="eventDescription" rows="3" placeholder="Describe your cosmic journey"></textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Event Type</label>
|
||
<select id="eventType">
|
||
<option value="planet">Planet (Single Event)</option>
|
||
<option value="comet">Comet (Recurring Weekly)</option>
|
||
<option value="asteroid">Asteroid (Recurring Monthly)</option>
|
||
<option value="star">Star (Important)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Celestial Color</label>
|
||
<div class="color-picker">
|
||
<div class="color-option" style="background: #e74c3c;" data-color="#e74c3c"></div>
|
||
<div class="color-option" style="background: #f39c12;" data-color="#f39c12"></div>
|
||
<div class="color-option" style="background: #f1c40f;" data-color="#f1c40f"></div>
|
||
<div class="color-option" style="background: #2ecc71;" data-color="#2ecc71"></div>
|
||
<div class="color-option" style="background: #3498db;" data-color="#3498db"></div>
|
||
<div class="color-option selected" style="background: #9b59b6;" data-color="#9b59b6"></div>
|
||
<div class="color-option" style="background: #1abc9c;" data-color="#1abc9c"></div>
|
||
<div class="color-option" style="background: #ecf0f1;" data-color="#ecf0f1"></div>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Reminder (Satellite Signal)</label>
|
||
<select id="eventReminder">
|
||
<option value="none">No Signal</option>
|
||
<option value="5">5 minutes before</option>
|
||
<option value="15">15 minutes before</option>
|
||
<option value="30">30 minutes before</option>
|
||
<option value="60">1 hour before</option>
|
||
<option value="1440">1 day before</option>
|
||
</select>
|
||
</div>
|
||
<button type="submit" class="save-btn">Launch Event</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Global variables
|
||
let currentDate = new Date();
|
||
let selectedDate = null;
|
||
let events = JSON.parse(localStorage.getItem('cosmicEvents')) || [];
|
||
let selectedColor = '#9b59b6';
|
||
|
||
// Time zones with planet names
|
||
const planetaryTimeZones = [
|
||
{ name: 'Mercury', offset: -8, color: '#8b7355' },
|
||
{ name: 'Venus', offset: -5, color: '#ffd700' },
|
||
{ name: 'Earth', offset: 0, color: '#4169e1' },
|
||
{ name: 'Mars', offset: 3, color: '#cd5c5c' },
|
||
{ name: 'Jupiter', offset: 6, color: '#daa520' },
|
||
{ name: 'Saturn', offset: 9, color: '#f4a460' }
|
||
];
|
||
|
||
// Initialize
|
||
function init() {
|
||
createStars();
|
||
updateTimeZones();
|
||
renderCalendar();
|
||
renderEvents();
|
||
setupEventListeners();
|
||
checkReminders();
|
||
createComets();
|
||
setInterval(updateTimeZones, 1000);
|
||
setInterval(checkReminders, 60000); // Check every minute
|
||
}
|
||
|
||
// Create stars
|
||
function createStars() {
|
||
const starsContainer = document.getElementById('starsContainer');
|
||
for (let i = 0; i < 200; i++) {
|
||
const star = document.createElement('div');
|
||
star.className = 'star';
|
||
star.style.left = Math.random() * 100 + '%';
|
||
star.style.top = Math.random() * 100 + '%';
|
||
star.style.width = star.style.height = Math.random() * 3 + 'px';
|
||
star.style.animationDelay = Math.random() * 3 + 's';
|
||
starsContainer.appendChild(star);
|
||
}
|
||
}
|
||
|
||
// Update time zones
|
||
function updateTimeZones() {
|
||
const container = document.getElementById('timeZones');
|
||
container.innerHTML = '';
|
||
|
||
planetaryTimeZones.forEach(zone => {
|
||
const now = new Date();
|
||
now.setHours(now.getHours() + zone.offset);
|
||
|
||
const planetDiv = document.createElement('div');
|
||
planetDiv.className = 'planet-time';
|
||
planetDiv.style.setProperty('--planet-color', zone.color);
|
||
planetDiv.innerHTML = `
|
||
<div class="planet-name">${zone.name}</div>
|
||
<div class="planet-time-display">${now.toLocaleTimeString()}</div>
|
||
`;
|
||
container.appendChild(planetDiv);
|
||
});
|
||
}
|
||
|
||
// Render calendar
|
||
function renderCalendar() {
|
||
const year = currentDate.getFullYear();
|
||
const month = currentDate.getMonth();
|
||
const firstDay = new Date(year, month, 1).getDay();
|
||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||
const prevDaysInMonth = new Date(year, month, 0).getDate();
|
||
|
||
document.getElementById('monthYear').textContent =
|
||
currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
||
|
||
const grid = document.getElementById('calendarGrid');
|
||
grid.innerHTML = '';
|
||
|
||
// Day headers
|
||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||
days.forEach(day => {
|
||
const header = document.createElement('div');
|
||
header.className = 'day-header';
|
||
header.textContent = day;
|
||
grid.appendChild(header);
|
||
});
|
||
|
||
// Previous month days
|
||
for (let i = firstDay - 1; i >= 0; i--) {
|
||
const day = prevDaysInMonth - i;
|
||
createDayElement(day, month - 1, year, true);
|
||
}
|
||
|
||
// Current month days
|
||
for (let day = 1; day <= daysInMonth; day++) {
|
||
createDayElement(day, month, year, false);
|
||
}
|
||
|
||
// Next month days
|
||
const totalCells = firstDay + daysInMonth;
|
||
const nextDays = totalCells > 35 ? 42 - totalCells : 35 - totalCells;
|
||
for (let day = 1; day <= nextDays; day++) {
|
||
createDayElement(day, month + 1, year, true);
|
||
}
|
||
|
||
// Draw constellation lines between events
|
||
drawConstellations();
|
||
}
|
||
|
||
// Create day element
|
||
function createDayElement(day, month, year, isOtherMonth) {
|
||
const dayDiv = document.createElement('div');
|
||
dayDiv.className = 'calendar-day';
|
||
if (isOtherMonth) dayDiv.classList.add('other-month');
|
||
|
||
const today = new Date();
|
||
const cellDate = new Date(year, month, day);
|
||
|
||
if (cellDate.toDateString() === today.toDateString()) {
|
||
dayDiv.classList.add('today');
|
||
}
|
||
|
||
dayDiv.innerHTML = `<div class="day-number">${day}</div>`;
|
||
|
||
// Add event indicators
|
||
const dayEvents = events.filter(event => {
|
||
const eventDate = new Date(event.date);
|
||
return eventDate.toDateString() === cellDate.toDateString();
|
||
});
|
||
|
||
dayEvents.forEach(event => {
|
||
const indicator = document.createElement('div');
|
||
indicator.className = 'event-indicator';
|
||
indicator.style.setProperty('--event-color', event.color);
|
||
dayDiv.appendChild(indicator);
|
||
});
|
||
|
||
dayDiv.onclick = () => selectDate(cellDate);
|
||
document.getElementById('calendarGrid').appendChild(dayDiv);
|
||
}
|
||
|
||
// Select date
|
||
function selectDate(date) {
|
||
selectedDate = date;
|
||
document.getElementById('eventDate').value = date.toISOString().split('T')[0];
|
||
openModal();
|
||
}
|
||
|
||
// Change month
|
||
function changeMonth(direction) {
|
||
currentDate.setMonth(currentDate.getMonth() + direction);
|
||
renderCalendar();
|
||
}
|
||
|
||
// Event management
|
||
function openModal() {
|
||
document.getElementById('eventModal').style.display = 'block';
|
||
createOrbitalPaths();
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('eventModal').style.display = 'none';
|
||
document.getElementById('eventForm').reset();
|
||
}
|
||
|
||
// Setup event listeners
|
||
function setupEventListeners() {
|
||
// Color picker
|
||
document.querySelectorAll('.color-option').forEach(option => {
|
||
option.addEventListener('click', function() {
|
||
document.querySelectorAll('.color-option').forEach(o => o.classList.remove('selected'));
|
||
this.classList.add('selected');
|
||
selectedColor = this.dataset.color;
|
||
});
|
||
});
|
||
|
||
// Form submission
|
||
document.getElementById('eventForm').addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const event = {
|
||
id: Date.now(),
|
||
name: document.getElementById('eventName').value,
|
||
date: document.getElementById('eventDate').value,
|
||
time: document.getElementById('eventTime').value,
|
||
description: document.getElementById('eventDescription').value,
|
||
type: document.getElementById('eventType').value,
|
||
color: selectedColor,
|
||
reminder: document.getElementById('eventReminder').value
|
||
};
|
||
|
||
events.push(event);
|
||
localStorage.setItem('cosmicEvents', JSON.stringify(events));
|
||
|
||
renderCalendar();
|
||
renderEvents();
|
||
closeModal();
|
||
|
||
// Create satellite signal effect
|
||
if (event.reminder !== 'none') {
|
||
createSatelliteSignal();
|
||
}
|
||
});
|
||
}
|
||
|
||
// Render events list
|
||
function renderEvents() {
|
||
const container = document.getElementById('eventsList');
|
||
container.innerHTML = '';
|
||
|
||
const today = new Date().toDateString();
|
||
const todayEvents = events.filter(event =>
|
||
new Date(event.date).toDateString() === today
|
||
);
|
||
|
||
todayEvents.forEach(event => {
|
||
const eventDiv = document.createElement('div');
|
||
eventDiv.className = 'event-item';
|
||
eventDiv.style.setProperty('--event-color', event.color);
|
||
eventDiv.innerHTML = `
|
||
<div class="event-title">${event.name}</div>
|
||
<div class="event-time">${event.time} - ${event.type}</div>
|
||
`;
|
||
container.appendChild(eventDiv);
|
||
});
|
||
}
|
||
|
||
// Check reminders
|
||
function checkReminders() {
|
||
const now = new Date();
|
||
|
||
events.forEach(event => {
|
||
if (event.reminder === 'none') return;
|
||
|
||
const eventDateTime = new Date(event.date + ' ' + event.time);
|
||
const reminderTime = new Date(eventDateTime.getTime() - event.reminder * 60000);
|
||
|
||
if (Math.abs(now - reminderTime) < 60000) {
|
||
showReminder(event);
|
||
}
|
||
});
|
||
}
|
||
|
||
// Show reminder
|
||
function showReminder(event) {
|
||
createSatelliteSignal();
|
||
|
||
if (Notification.permission === 'granted') {
|
||
new Notification('Cosmic Calendar Reminder', {
|
||
body: `${event.name} starts in ${event.reminder} minutes`,
|
||
icon: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="%234a90e2"/></svg>'
|
||
});
|
||
}
|
||
}
|
||
|
||
// Create satellite signal effect
|
||
function createSatelliteSignal() {
|
||
const signal = document.createElement('div');
|
||
signal.className = 'satellite-signal';
|
||
signal.style.left = Math.random() * window.innerWidth + 'px';
|
||
signal.style.top = Math.random() * window.innerHeight + 'px';
|
||
document.body.appendChild(signal);
|
||
|
||
setTimeout(() => signal.remove(), 3000);
|
||
}
|
||
|
||
// Create comets for recurring events
|
||
function createComets() {
|
||
setInterval(() => {
|
||
const recurringEvents = events.filter(e =>
|
||
e.type === 'comet' || e.type === 'asteroid'
|
||
);
|
||
|
||
if (recurringEvents.length > 0 && Math.random() < 0.3) {
|
||
const comet = document.createElement('div');
|
||
comet.className = 'comet';
|
||
comet.innerHTML = '<div class="comet-trail"></div>';
|
||
|
||
const startY = Math.random() * window.innerHeight;
|
||
comet.style.top = startY + 'px';
|
||
comet.style.right = '-10px';
|
||
|
||
document.body.appendChild(comet);
|
||
|
||
// Animate comet
|
||
let position = -10;
|
||
const animation = setInterval(() => {
|
||
position += 5;
|
||
comet.style.right = position + 'px';
|
||
|
||
if (position > window.innerWidth + 100) {
|
||
clearInterval(animation);
|
||
comet.remove();
|
||
}
|
||
}, 20);
|
||
}
|
||
}, 5000);
|
||
}
|
||
|
||
// Create orbital paths in modal
|
||
function createOrbitalPaths() {
|
||
const modal = document.querySelector('.modal-content');
|
||
|
||
for (let i = 0; i < 3; i++) {
|
||
const path = document.createElement('div');
|
||
path.className = 'orbital-path';
|
||
path.style.width = (i + 1) * 150 + 'px';
|
||
path.style.height = (i + 1) * 150 + 'px';
|
||
path.style.top = '50%';
|
||
path.style.left = '50%';
|
||
path.style.transform = 'translate(-50%, -50%)';
|
||
path.style.animationDuration = (i + 1) * 10 + 's';
|
||
modal.appendChild(path);
|
||
|
||
setTimeout(() => path.remove(), 5000);
|
||
}
|
||
}
|
||
|
||
// Draw constellation lines between events
|
||
function drawConstellations() {
|
||
const dayElements = document.querySelectorAll('.calendar-day:not(.other-month)');
|
||
const eventDays = [];
|
||
|
||
dayElements.forEach(day => {
|
||
if (day.querySelector('.event-indicator')) {
|
||
eventDays.push(day);
|
||
}
|
||
});
|
||
|
||
// Connect nearby events
|
||
for (let i = 0; i < eventDays.length - 1; i++) {
|
||
for (let j = i + 1; j < eventDays.length; j++) {
|
||
if (Math.random() < 0.3) { // 30% chance to connect
|
||
connectDays(eventDays[i], eventDays[j]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Connect two days with a line
|
||
function connectDays(day1, day2) {
|
||
const rect1 = day1.getBoundingClientRect();
|
||
const rect2 = day2.getBoundingClientRect();
|
||
const container = document.querySelector('.calendar-grid');
|
||
const containerRect = container.getBoundingClientRect();
|
||
|
||
const x1 = rect1.left + rect1.width / 2 - containerRect.left;
|
||
const y1 = rect1.top + rect1.height / 2 - containerRect.top;
|
||
const x2 = rect2.left + rect2.width / 2 - containerRect.left;
|
||
const y2 = rect2.top + rect2.height / 2 - containerRect.top;
|
||
|
||
const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
||
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
|
||
|
||
const line = document.createElement('div');
|
||
line.className = 'constellation-line';
|
||
line.style.width = distance + 'px';
|
||
line.style.left = x1 + 'px';
|
||
line.style.top = y1 + 'px';
|
||
line.style.transform = `rotate(${angle}deg)`;
|
||
|
||
container.appendChild(line);
|
||
}
|
||
|
||
// Request notification permission
|
||
if ('Notification' in window && Notification.permission === 'default') {
|
||
Notification.requestPermission();
|
||
}
|
||
|
||
// Initialize on load
|
||
window.onload = init;
|
||
|
||
// Handle window resize
|
||
window.addEventListener('resize', () => {
|
||
renderCalendar();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |