openfree commited on
Commit
1bd4bc0
·
verified ·
1 Parent(s): c6207dc

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +332 -242
index.html CHANGED
@@ -3,326 +3,416 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Memory Match Game</title>
7
  <style>
8
- :root {
9
- --card-width: 120px;
10
- --card-height: 150px;
11
- --primary-color: #2c3e50;
12
- --secondary-color: #3498db;
13
- }
14
-
15
  * {
16
- box-sizing: border-box;
17
  margin: 0;
18
  padding: 0;
 
 
19
  }
20
 
21
  body {
22
- min-height: 100vh;
23
- background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
24
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
25
- display: flex;
26
- flex-direction: column;
27
- align-items: center;
28
- color: white;
29
  }
30
 
31
- .game-container {
32
- margin-top: 2rem;
33
- text-align: center;
 
 
 
 
34
  }
35
 
36
- .controls {
37
- margin: 1rem 0;
38
  display: flex;
39
- gap: 1rem;
40
- justify-content: center;
41
  align-items: center;
 
42
  }
43
 
44
- .btn {
45
- padding: 0.5rem 1rem;
 
 
 
 
 
 
46
  border: none;
47
- border-radius: 5px;
48
- background: rgba(255, 255, 255, 0.2);
49
- color: white;
50
  cursor: pointer;
51
- transition: all 0.3s ease;
 
52
  }
53
 
54
- .btn:hover {
55
- background: rgba(255, 255, 255, 0.3);
 
56
  }
57
 
58
- .stats {
59
  display: flex;
60
- gap: 2rem;
61
- margin: 1rem;
62
- font-size: 1.2rem;
63
  }
64
 
65
- .board {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  display: grid;
 
67
  gap: 10px;
68
- padding: 20px;
69
- perspective: 1000px;
70
  }
71
 
72
- .card {
73
- width: var(--card-width);
74
- height: var(--card-height);
75
- position: relative;
76
- transform-style: preserve-3d;
77
- transition: transform 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
78
- cursor: pointer;
79
  }
80
 
81
- .card.flipped {
82
- transform: rotateY(180deg);
 
 
 
 
 
83
  }
84
 
85
- .card.matched {
86
- transform: rotateY(180deg) scale(0.95);
87
- opacity: 0.8;
88
  }
89
 
90
- .card-face {
91
- position: absolute;
92
- width: 100%;
93
- height: 100%;
94
- backface-visibility: hidden;
95
- border-radius: 10px;
96
- display: flex;
97
- align-items: center;
98
- justify-content: center;
99
- font-size: 2rem;
100
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
 
103
- .card-front {
104
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
105
- transform: rotateY(180deg);
 
 
 
 
106
  }
107
 
108
- .card-back {
109
- background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
  .modal {
 
113
  position: fixed;
114
- top: 50%;
115
- left: 50%;
116
- transform: translate(-50%, -50%);
117
- background: rgba(0, 0, 0, 0.9);
118
- padding: 2rem;
 
 
 
 
 
 
 
119
  border-radius: 10px;
120
- text-align: center;
121
- display: none;
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
- @keyframes match {
125
- 0%, 100% { transform: rotateY(180deg) scale(1); }
126
- 50% { transform: rotateY(180deg) scale(1.1); }
 
 
127
  }
128
 
129
- @keyframes mismatch {
130
- 0%, 100% { transform: rotateY(180deg); }
131
- 20%, 40%, 60%, 80% { transform: rotateY(180deg) translateX(-10px); }
132
- 30%, 50%, 70%, 90% { transform: rotateY(180deg) translateX(10px); }
 
133
  }
 
 
 
134
  </style>
135
  </head>
136
  <body>
137
- <div class="game-container">
138
- <h1>Memory Match</h1>
139
- <div class="controls">
140
- <select id="difficulty" class="btn">
141
- <option value="8">Easy (4x4)</option>
142
- <option value="18">Medium (6x6)</option>
143
- <option value="32">Hard (8x8)</option>
144
- </select>
145
- <button class="btn" id="start">New Game</button>
146
- </div>
147
- <div class="stats">
148
- <div>Moves: <span id="moves">0</span></div>
149
- <div>Time: <span id="timer">0:00</span></div>
150
- <div>Score: <span id="score">0</span></div>
151
  </div>
152
- <div class="board" id="board"></div>
153
  </div>
154
- <div class="modal" id="win-modal">
155
- <h2>Congratulations!</h2>
156
- <p>You completed the game in <span id="final-time"></span></p>
157
- <p>Final Score: <span id="final-score"></span></p>
158
- <button class="btn" onclick="startGame()">Play Again</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  </div>
160
 
161
  <script>
162
- const audioContext = new (window.AudioContext || window.webkitAudioContext)();
163
- let gameState = {
164
- flippedCards: [],
165
- matches: 0,
166
- moves: 0,
167
- score: 0,
168
- timer: 0,
169
- gameStarted: false,
170
- timerInterval: null
171
- };
172
-
173
- const emojis = ['🎮', '🎲', '🎯', '🎨', '🎭', '🎪', '🎫', '🎬',
174
- '🎤', '🎧', '🎷', '🎸', '🎹', '🎺', '🎻', '🥁',
175
- '⚽️', '🏀', '🏈', '⚾️', '🎾', '🏐', '🏉', '🎱',
176
- '🏓', '🏸', '🥊', '🥋', '🎣', '⛳️', '🏹', '🎽'];
177
-
178
- function createAudioBuffer(frequency, duration) {
179
- const sampleRate = audioContext.sampleRate;
180
- const buffer = audioContext.createBuffer(1, duration * sampleRate, sampleRate);
181
- const data = buffer.getChannelData(0);
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
- for (let i = 0; i < buffer.length; i++) {
184
- data[i] = Math.sin(2 * Math.PI * frequency * i / sampleRate) *
185
- Math.exp(-5 * i / buffer.length);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
-
188
- return buffer;
189
  }
190
 
191
- const matchSound = createAudioBuffer(440, 0.1);
192
- const mismatchSound = createAudioBuffer(220, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
- function playSound(buffer) {
195
- const source = audioContext.createBufferSource();
196
- source.buffer = buffer;
197
- source.connect(audioContext.destination);
198
- source.start();
199
  }
200
 
201
- function createBoard() {
202
- const board = document.getElementById('board');
203
- const difficulty = parseInt(document.getElementById('difficulty').value);
204
- const pairs = difficulty;
205
- const size = Math.sqrt(pairs * 2);
 
 
 
 
 
 
206
 
207
- board.style.gridTemplateColumns = `repeat(${size}, var(--card-width))`;
208
- board.innerHTML = '';
209
-
210
- const selectedEmojis = emojis.slice(0, pairs);
211
- const cards = [...selectedEmojis, ...selectedEmojis]
212
- .sort(() => Math.random() - 0.5);
213
-
214
- cards.forEach((emoji, index) => {
215
- const card = document.createElement('div');
216
- card.className = 'card';
217
- card.dataset.value = emoji;
218
- card.dataset.index = index;
219
- card.innerHTML = `
220
- <div class="card-face card-front">${emoji}</div>
221
- <div class="card-face card-back">?</div>
222
- `;
223
- card.addEventListener('click', flipCard);
224
- board.appendChild(card);
225
  });
 
 
226
  }
227
 
228
- function flipCard() {
229
- if (!gameState.gameStarted) {
230
- startTimer();
231
- gameState.gameStarted = true;
232
- }
233
 
234
- const card = this;
235
- if (card.classList.contains('flipped') ||
236
- gameState.flippedCards.length >= 2) return;
237
 
238
- card.classList.add('flipped');
239
- gameState.flippedCards.push(card);
 
 
 
 
240
 
241
- if (gameState.flippedCards.length === 2) {
242
- gameState.moves++;
243
- document.getElementById('moves').textContent = gameState.moves;
244
- checkMatch();
245
- }
246
  }
247
 
248
- function checkMatch() {
249
- const [card1, card2] = gameState.flippedCards;
250
- const match = card1.dataset.value === card2.dataset.value;
251
-
252
- if (match) {
253
- playSound(matchSound);
254
- card1.classList.add('matched');
255
- card2.classList.add('matched');
256
- gameState.matches++;
257
- gameState.score += 100;
258
- card1.style.animation = card2.style.animation = 'match 0.5s ease';
259
-
260
- if (gameState.matches === parseInt(document.getElementById('difficulty').value)) {
261
- endGame();
262
- }
263
- } else {
264
- playSound(mismatchSound);
265
- card1.style.animation = card2.style.animation = 'mismatch 0.5s ease';
266
- gameState.score = Math.max(0, gameState.score - 20);
267
-
268
- setTimeout(() => {
269
- card1.classList.remove('flipped');
270
- card2.classList.remove('flipped');
271
- }, 1000);
272
- }
273
 
274
- setTimeout(() => {
275
- card1.style.animation = card2.style.animation = '';
276
- }, 500);
277
-
278
- document.getElementById('score').textContent = gameState.score;
279
- gameState.flippedCards = [];
280
- }
281
-
282
- function startTimer() {
283
- gameState.timerInterval = setInterval(() => {
284
- gameState.timer++;
285
- const minutes = Math.floor(gameState.timer / 60);
286
- const seconds = gameState.timer % 60;
287
- document.getElementById('timer').textContent =
288
- `${minutes}:${seconds.toString().padStart(2, '0')}`;
289
- }, 1000);
290
- }
291
-
292
- function endGame() {
293
- clearInterval(gameState.timerInterval);
294
- const modal = document.getElementById('win-modal');
295
- document.getElementById('final-time').textContent =
296
- document.getElementById('timer').textContent;
297
- document.getElementById('final-score').textContent = gameState.score;
298
- modal.style.display = 'block';
299
- }
300
-
301
- function startGame() {
302
- gameState = {
303
- flippedCards: [],
304
- matches: 0,
305
- moves: 0,
306
- score: 0,
307
- timer: 0,
308
- gameStarted: false,
309
- timerInterval: null
310
- };
311
 
312
- document.getElementById('moves').textContent = '0';
313
- document.getElementById('timer').textContent = '0:00';
314
- document.getElementById('score').textContent = '0';
315
- document.getElementById('win-modal').style.display = 'none';
 
 
 
316
 
317
- createBoard();
318
- clearInterval(gameState.timerInterval);
 
 
 
 
 
 
 
 
 
 
 
319
  }
320
 
321
- document.getElementById('start').addEventListener('click', startGame);
322
- document.getElementById('difficulty').addEventListener('change', startGame);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
- // Initial game setup
325
- startGame();
326
  </script>
327
  </body>
328
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Interactive Calendar</title>
7
  <style>
 
 
 
 
 
 
 
8
  * {
 
9
  margin: 0;
10
  padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: 'Arial', sans-serif;
13
  }
14
 
15
  body {
16
+ background: #f5f5f5;
17
+ padding: 20px;
 
 
 
 
 
18
  }
19
 
20
+ .calendar-container {
21
+ max-width: 1200px;
22
+ margin: 0 auto;
23
+ background: white;
24
+ border-radius: 10px;
25
+ box-shadow: 0 5px 20px rgba(0,0,0,0.1);
26
+ padding: 20px;
27
  }
28
 
29
+ .calendar-header {
 
30
  display: flex;
31
+ justify-content: space-between;
 
32
  align-items: center;
33
+ margin-bottom: 20px;
34
  }
35
 
36
+ .month-nav {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 20px;
40
+ }
41
+
42
+ .nav-btn {
43
+ background: none;
44
  border: none;
45
+ font-size: 24px;
 
 
46
  cursor: pointer;
47
+ color: #333;
48
+ padding: 5px 10px;
49
  }
50
 
51
+ .current-month {
52
+ font-size: 24px;
53
+ font-weight: bold;
54
  }
55
 
56
+ .filter-controls {
57
  display: flex;
58
+ gap: 10px;
 
 
59
  }
60
 
61
+ .filter-btn {
62
+ padding: 8px 15px;
63
+ border: none;
64
+ border-radius: 20px;
65
+ cursor: pointer;
66
+ background: #eee;
67
+ transition: 0.3s;
68
+ }
69
+
70
+ .filter-btn.active {
71
+ background: #007bff;
72
+ color: white;
73
+ }
74
+
75
+ .calendar-grid {
76
  display: grid;
77
+ grid-template-columns: repeat(7, 1fr);
78
  gap: 10px;
 
 
79
  }
80
 
81
+ .day-header {
82
+ text-align: center;
83
+ font-weight: bold;
84
+ padding: 10px;
85
+ background: #f8f9fa;
86
+ border-radius: 5px;
 
87
  }
88
 
89
+ .day-cell {
90
+ min-height: 120px;
91
+ padding: 10px;
92
+ background: #fff;
93
+ border: 1px solid #eee;
94
+ border-radius: 5px;
95
+ transition: 0.3s;
96
  }
97
 
98
+ .day-cell:hover {
99
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
 
100
  }
101
 
102
+ .day-cell.different-month {
103
+ background: #f8f9fa;
104
+ color: #999;
105
+ }
106
+
107
+ .event {
108
+ margin: 5px 0;
109
+ padding: 5px 10px;
110
+ border-radius: 3px;
111
+ font-size: 12px;
112
+ cursor: move;
113
+ animation: slideIn 0.3s ease-out;
114
+ }
115
+
116
+ @keyframes slideIn {
117
+ from {
118
+ transform: translateY(-10px);
119
+ opacity: 0;
120
+ }
121
+ to {
122
+ transform: translateY(0);
123
+ opacity: 1;
124
+ }
125
  }
126
 
127
+ .event.work { background: #ffcdd2; }
128
+ .event.personal { background: #c8e6c9; }
129
+ .event.meeting { background: #bbdefb; }
130
+
131
+ .event.dragging {
132
+ opacity: 0.5;
133
+ transform: scale(0.95);
134
  }
135
 
136
+ .add-event-btn {
137
+ position: fixed;
138
+ bottom: 30px;
139
+ right: 30px;
140
+ width: 60px;
141
+ height: 60px;
142
+ border-radius: 50%;
143
+ background: #007bff;
144
+ color: white;
145
+ border: none;
146
+ font-size: 24px;
147
+ cursor: pointer;
148
+ box-shadow: 0 3px 10px rgba(0,0,0,0.2);
149
+ transition: 0.3s;
150
+ }
151
+
152
+ .add-event-btn:hover {
153
+ transform: scale(1.1);
154
  }
155
 
156
  .modal {
157
+ display: none;
158
  position: fixed;
159
+ top: 0;
160
+ left: 0;
161
+ width: 100%;
162
+ height: 100%;
163
+ background: rgba(0,0,0,0.5);
164
+ justify-content: center;
165
+ align-items: center;
166
+ }
167
+
168
+ .modal-content {
169
+ background: white;
170
+ padding: 20px;
171
  border-radius: 10px;
172
+ width: 90%;
173
+ max-width: 400px;
174
+ }
175
+
176
+ .form-group {
177
+ margin: 15px 0;
178
+ }
179
+
180
+ input, select {
181
+ width: 100%;
182
+ padding: 8px;
183
+ border: 1px solid #ddd;
184
+ border-radius: 5px;
185
  }
186
 
187
+ .modal-buttons {
188
+ display: flex;
189
+ justify-content: flex-end;
190
+ gap: 10px;
191
+ margin-top: 20px;
192
  }
193
 
194
+ .modal-btn {
195
+ padding: 8px 15px;
196
+ border: none;
197
+ border-radius: 5px;
198
+ cursor: pointer;
199
  }
200
+
201
+ .save-btn { background: #007bff; color: white; }
202
+ .cancel-btn { background: #eee; }
203
  </style>
204
  </head>
205
  <body>
206
+ <div class="calendar-container">
207
+ <div class="calendar-header">
208
+ <div class="month-nav">
209
+ <button class="nav-btn" onclick="changeMonth(-1)">←</button>
210
+ <div class="current-month"></div>
211
+ <button class="nav-btn" onclick="changeMonth(1)">→</button>
212
+ </div>
213
+ <div class="filter-controls">
214
+ <button class="filter-btn active" data-type="all">All</button>
215
+ <button class="filter-btn" data-type="work">Work</button>
216
+ <button class="filter-btn" data-type="personal">Personal</button>
217
+ <button class="filter-btn" data-type="meeting">Meeting</button>
218
+ </div>
 
219
  </div>
220
+ <div class="calendar-grid"></div>
221
  </div>
222
+
223
+ <button class="add-event-btn" onclick="openModal()">+</button>
224
+
225
+ <div class="modal">
226
+ <div class="modal-content">
227
+ <h2>Add Event</h2>
228
+ <div class="form-group">
229
+ <input type="text" id="eventTitle" placeholder="Event Title">
230
+ </div>
231
+ <div class="form-group">
232
+ <select id="eventType">
233
+ <option value="work">Work</option>
234
+ <option value="personal">Personal</option>
235
+ <option value="meeting">Meeting</option>
236
+ </select>
237
+ </div>
238
+ <div class="modal-buttons">
239
+ <button class="modal-btn cancel-btn" onclick="closeModal()">Cancel</button>
240
+ <button class="modal-btn save-btn" onclick="saveEvent()">Save</button>
241
+ </div>
242
+ </div>
243
  </div>
244
 
245
  <script>
246
+ let currentDate = new Date();
247
+ let events = [];
248
+ let selectedDate = null;
249
+ let draggedEvent = null;
250
+
251
+ function initCalendar() {
252
+ updateMonthDisplay();
253
+ renderCalendar();
254
+ initEventListeners();
255
+ }
256
+
257
+ function updateMonthDisplay() {
258
+ const months = ['January', 'February', 'March', 'April', 'May', 'June',
259
+ 'July', 'August', 'September', 'October', 'November', 'December'];
260
+ document.querySelector('.current-month').textContent =
261
+ `${months[currentDate.getMonth()]} ${currentDate.getFullYear()}`;
262
+ }
263
+
264
+ function renderCalendar() {
265
+ const grid = document.querySelector('.calendar-grid');
266
+ grid.innerHTML = '';
267
+
268
+ // Add day headers
269
+ const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
270
+ days.forEach(day => {
271
+ const dayHeader = document.createElement('div');
272
+ dayHeader.className = 'day-header';
273
+ dayHeader.textContent = day;
274
+ grid.appendChild(dayHeader);
275
+ });
276
+
277
+ const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
278
+ const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
279
 
280
+ // Add padding days from previous month
281
+ const paddingDays = firstDay.getDay();
282
+ const prevLastDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0);
283
+ for (let i = paddingDays - 1; i >= 0; i--) {
284
+ const dayCell = createDayCell(prevLastDay.getDate() - i, true);
285
+ grid.appendChild(dayCell);
286
+ }
287
+
288
+ // Add current month days
289
+ for (let day = 1; day <= lastDay.getDate(); day++) {
290
+ const dayCell = createDayCell(day, false);
291
+ grid.appendChild(dayCell);
292
+ }
293
+
294
+ // Add padding days for next month
295
+ const remainingDays = 42 - (paddingDays + lastDay.getDate());
296
+ for (let i = 1; i <= remainingDays; i++) {
297
+ const dayCell = createDayCell(i, true);
298
+ grid.appendChild(dayCell);
299
  }
 
 
300
  }
301
 
302
+ function createDayCell(day, isDifferentMonth) {
303
+ const cell = document.createElement('div');
304
+ cell.className = `day-cell${isDifferentMonth ? ' different-month' : ''}`;
305
+ cell.innerHTML = `<div class="day-number">${day}</div>`;
306
+ cell.addEventListener('dragover', e => e.preventDefault());
307
+ cell.addEventListener('drop', handleDrop);
308
+
309
+ // Add events for this day
310
+ if (!isDifferentMonth) {
311
+ const date = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
312
+ const dayEvents = events.filter(event => {
313
+ const eventDate = new Date(event.date);
314
+ return eventDate.toDateString() === date.toDateString();
315
+ });
316
+
317
+ dayEvents.forEach(event => {
318
+ const eventElement = createEventElement(event);
319
+ cell.appendChild(eventElement);
320
+ });
321
+ }
322
 
323
+ return cell;
 
 
 
 
324
  }
325
 
326
+ function createEventElement(event) {
327
+ const eventDiv = document.createElement('div');
328
+ eventDiv.className = `event ${event.type}`;
329
+ eventDiv.textContent = event.title;
330
+ eventDiv.draggable = true;
331
+ eventDiv.dataset.eventId = event.id;
332
+
333
+ eventDiv.addEventListener('dragstart', e => {
334
+ draggedEvent = event;
335
+ e.target.classList.add('dragging');
336
+ });
337
 
338
+ eventDiv.addEventListener('dragend', e => {
339
+ e.target.classList.remove('dragging');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  });
341
+
342
+ return eventDiv;
343
  }
344
 
345
+ function handleDrop(e) {
346
+ e.preventDefault();
347
+ if (!draggedEvent) return;
 
 
348
 
349
+ const dayCell = e.target.closest('.day-cell');
350
+ if (!dayCell) return;
 
351
 
352
+ const dayNumber = dayCell.querySelector('.day-number').textContent;
353
+ const newDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), dayNumber);
354
+
355
+ // Update event date
356
+ const eventIndex = events.findIndex(e => e.id === draggedEvent.id);
357
+ events[eventIndex].date = newDate;
358
 
359
+ draggedEvent = null;
360
+ renderCalendar();
 
 
 
361
  }
362
 
363
+ function changeMonth(delta) {
364
+ currentDate.setMonth(currentDate.getMonth() + delta);
365
+ updateMonthDisplay();
366
+ renderCalendar();
367
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
 
369
+ function openModal() {
370
+ document.querySelector('.modal').style.display = 'flex';
371
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
 
373
+ function closeModal() {
374
+ document.querySelector('.modal').style.display = 'none';
375
+ }
376
+
377
+ function saveEvent() {
378
+ const title = document.getElementById('eventTitle').value;
379
+ const type = document.getElementById('eventType').value;
380
 
381
+ if (!title) return;
382
+
383
+ const newEvent = {
384
+ id: Date.now(),
385
+ title,
386
+ type,
387
+ date: new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate())
388
+ };
389
+
390
+ events.push(newEvent);
391
+ renderCalendar();
392
+ closeModal();
393
+ document.getElementById('eventTitle').value = '';
394
  }
395
 
396
+ function initEventListeners() {
397
+ document.querySelectorAll('.filter-btn').forEach(btn => {
398
+ btn.addEventListener('click', (e) => {
399
+ document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
400
+ e.target.classList.add('active');
401
+
402
+ const type = e.target.dataset.type;
403
+ document.querySelectorAll('.event').forEach(event => {
404
+ if (type === 'all' || event.classList.contains(type)) {
405
+ event.style.display = 'block';
406
+ } else {
407
+ event.style.display = 'none';
408
+ }
409
+ });
410
+ });
411
+ });
412
+ }
413
 
414
+ // Initialize calendar
415
+ initCalendar();
416
  </script>
417
  </body>
418
  </html>