diff --git a/MemoryDiary/index.html b/MemoryDiary/index.html new file mode 100644 index 000000000..bd9dcaffd --- /dev/null +++ b/MemoryDiary/index.html @@ -0,0 +1,102 @@ + + + + + + MemoryDiary - Your Encrypted Diary + + + + + +
+
+
+
+

MemoryDiary

+

Your thoughts, encrypted and secure

+
+ +
+
+

Welcome to Your Diary

+
+ + + This will encrypt your diary. Make it strong and don't forget it! +
+ +
+
+ + + + + +
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/MemoryDiary/script.js b/MemoryDiary/script.js new file mode 100644 index 000000000..cdf02007b --- /dev/null +++ b/MemoryDiary/script.js @@ -0,0 +1,348 @@ +document.addEventListener('DOMContentLoaded', function() { + // DOM Elements + const authPanel = document.getElementById('authPanel'); + const mainApp = document.getElementById('mainApp'); + const initBtn = document.getElementById('initBtn'); + const passphraseInput = document.getElementById('passphrase'); + const newEntryBtn = document.getElementById('newEntryBtn'); + const entryTitle = document.getElementById('entryTitle'); + const entryContent = document.getElementById('entryContent'); + const saveBtn = document.getElementById('saveBtn'); + const deleteBtn = document.getElementById('deleteBtn'); + const entriesContainer = document.getElementById('entriesContainer'); + const searchInput = document.getElementById('searchInput'); + const wordCount = document.getElementById('wordCount'); + const notification = document.getElementById('notification'); + const notificationText = document.getElementById('notificationText'); + const unlockModal = document.getElementById('unlockModal'); + const unlockPassphraseInput = document.getElementById('unlockPassphrase'); + const unlockBtn = document.getElementById('unlockBtn'); + const cancelUnlockBtn = document.getElementById('cancelUnlockBtn'); + + // App State + let currentEntryId = null; + let entries = []; + let userPassphrase = ''; + let entryToUnlock = null; + + // Initialize the application + function init() { + // Check if diary is already initialized + const isInitialized = localStorage.getItem('memoryDiary_initialized'); + if (isInitialized) { + // Diary is already initialized, show unlock screen + showAuthPanel(); + document.querySelector('.auth-container h2').textContent = 'Unlock Your Diary'; + document.querySelector('.input-group label').textContent = 'Enter Your Passphrase'; + document.querySelector('.input-group small').textContent = 'Enter your passphrase to unlock your diary'; + initBtn.textContent = 'Unlock Diary'; + + // Change button event to unlock instead of initialize + initBtn.removeEventListener('click', initializeDiary); + initBtn.addEventListener('click', unlockDiary); + } else { + // Diary needs to be initialized + showAuthPanel(); + initBtn.addEventListener('click', initializeDiary); + } + + // Event listeners + newEntryBtn.addEventListener('click', createNewEntry); + saveBtn.addEventListener('click', saveEntry); + deleteBtn.addEventListener('click', deleteEntry); + searchInput.addEventListener('input', filterEntries); + entryContent.addEventListener('input', updateWordCount); + unlockBtn.addEventListener('click', unlockEntry); + cancelUnlockBtn.addEventListener('click', hideUnlockModal); + } + + // Show notification + function showNotification(message, isSuccess = true) { + notificationText.textContent = message; + notification.classList.add('show'); + notification.style.background = isSuccess ? '#8b5a2b' : '#a67b5b'; + + setTimeout(() => { + notification.classList.remove('show'); + }, 3000); + } + + // Show auth panel + function showAuthPanel() { + authPanel.classList.remove('hidden'); + mainApp.classList.add('hidden'); + } + + // Show main app + function showMainApp() { + authPanel.classList.add('hidden'); + mainApp.classList.remove('hidden'); + } + + // Show unlock modal + function showUnlockModal(entry) { + entryToUnlock = entry; + unlockModal.classList.remove('hidden'); + unlockPassphraseInput.value = ''; + unlockPassphraseInput.focus(); + } + + // Hide unlock modal + function hideUnlockModal() { + unlockModal.classList.add('hidden'); + entryToUnlock = null; + } + + // Initialize diary with passphrase + function initializeDiary() { + const passphraseValue = passphraseInput.value.trim(); + + if (passphraseValue.length < 8) { + showNotification('Passphrase must be at least 8 characters long', false); + return; + } + + // Store the initialization flag + localStorage.setItem('memoryDiary_initialized', 'true'); + + // Set passphrase and show main app + userPassphrase = passphraseValue; + showNotification('Diary initialized successfully!'); + showMainApp(); + + // Create a first entry + createNewEntry(); + } + + // Unlock diary with passphrase + function unlockDiary() { + const passphraseValue = passphraseInput.value.trim(); + + if (passphraseValue.length < 8) { + showNotification('Please enter your passphrase', false); + return; + } + + // Set passphrase and show main app + userPassphrase = passphraseValue; + + // Try to load entries to verify passphrase is correct + try { + loadEntries(); + showNotification('Diary unlocked successfully!'); + showMainApp(); + } catch (e) { + showNotification('Incorrect passphrase. Please try again.', false); + userPassphrase = ''; + } + } + + // Encrypt text + function encryptText(text) { + if (!userPassphrase) return text; + return CryptoJS.AES.encrypt(text, userPassphrase).toString(); + } + + // Decrypt text + function decryptText(ciphertext) { + if (!userPassphrase) return ciphertext; + try { + const bytes = CryptoJS.AES.decrypt(ciphertext, userPassphrase); + const decrypted = bytes.toString(CryptoJS.enc.Utf8); + + if (!decrypted) { + throw new Error('Decryption failed'); + } + + return decrypted; + } catch (e) { + console.error('Decryption error:', e); + throw new Error('Error decrypting content. Wrong passphrase?'); + } + } + + // Create a new entry + function createNewEntry() { + currentEntryId = Date.now().toString(); + entryTitle.value = ''; + entryContent.value = ''; + updateWordCount(); + + // Remove active class from all entries + document.querySelectorAll('.entry-item').forEach(item => { + item.classList.remove('active'); + }); + } + + // Save entry + function saveEntry() { + const title = entryTitle.value.trim(); + const content = entryContent.value.trim(); + + if (!title || !content) { + showNotification('Please add both title and content', false); + return; + } + + // Encrypt the content + const encryptedContent = encryptText(content); + const encryptedTitle = encryptText(title); + + // Create or update entry + const entryIndex = entries.findIndex(entry => entry.id === currentEntryId); + const now = new Date(); + + if (entryIndex !== -1) { + // Update existing entry + entries[entryIndex] = { + ...entries[entryIndex], + title: encryptedTitle, + content: encryptedContent, + date: now.toISOString() + }; + } else { + // Create new entry + entries.push({ + id: currentEntryId, + title: encryptedTitle, + content: encryptedContent, + date: now.toISOString() + }); + } + + // Save to localStorage + localStorage.setItem('memoryDiary_entries', JSON.stringify(entries)); + + // Update UI + loadEntries(); + showNotification('Entry saved and encrypted successfully!'); + } + + // Delete entry + function deleteEntry() { + if (!currentEntryId) return; + + if (confirm('Are you sure you want to delete this entry?')) { + entries = entries.filter(entry => entry.id !== currentEntryId); + localStorage.setItem('memoryDiary_entries', JSON.stringify(entries)); + + createNewEntry(); + loadEntries(); + showNotification('Entry deleted successfully'); + } + } + + // Load entries + function loadEntries() { + const savedEntries = localStorage.getItem('memoryDiary_entries'); + if (savedEntries) { + entries = JSON.parse(savedEntries); + } + + // Clear entries container + entriesContainer.innerHTML = ''; + + // Sort entries by date (newest first) + entries.sort((a, b) => new Date(b.date) - new Date(a.date)); + + // Add entries to the list + entries.forEach(entry => { + const entryDate = new Date(entry.date); + const formattedDate = entryDate.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + + // For security, we don't decrypt until user clicks + const entryElement = document.createElement('div'); + entryElement.classList.add('entry-item'); + entryElement.setAttribute('data-entry-id', entry.id); + if (entry.id === currentEntryId) { + entryElement.classList.add('active'); + } + + entryElement.innerHTML = ` +
Encrypted Entry
+
${formattedDate}
+
Click to unlock and view
+ `; + + entryElement.addEventListener('click', () => { + showUnlockModal(entry); + }); + + entriesContainer.appendChild(entryElement); + }); + } + + // Unlock entry with passphrase + function unlockEntry() { + const passphraseValue = unlockPassphraseInput.value.trim(); + + if (!passphraseValue) { + showNotification('Please enter your passphrase', false); + return; + } + + // Temporarily set the passphrase + const tempPassphrase = userPassphrase; + userPassphrase = passphraseValue; + + try { + // Try to decrypt the entry + const decryptedTitle = decryptText(entryToUnlock.title); + const decryptedContent = decryptText(entryToUnlock.content); + + // Success - load the entry + currentEntryId = entryToUnlock.id; + entryTitle.value = decryptedTitle; + entryContent.value = decryptedContent; + updateWordCount(); + + // Update active entry in list + document.querySelectorAll('.entry-item').forEach(item => { + item.classList.remove('active'); + }); + + // Update the entry element to show the actual title + const entryElement = document.querySelector(`.entry-item[data-entry-id="${entryToUnlock.id}"]`); + if (entryElement) { + entryElement.querySelector('.entry-title').textContent = decryptedTitle; + entryElement.querySelector('.entry-preview').textContent = decryptedContent.substring(0, 50) + '...'; + entryElement.classList.add('active'); + } + + hideUnlockModal(); + showNotification('Entry unlocked successfully!'); + } catch (e) { + // Failed to decrypt + showNotification('Incorrect passphrase. Please try again.', false); + userPassphrase = tempPassphrase; // Restore original passphrase + } + } + + // Filter entries based on search input + function filterEntries() { + const searchTerm = searchInput.value.toLowerCase(); + + document.querySelectorAll('.entry-item').forEach(item => { + const title = item.querySelector('.entry-title').textContent.toLowerCase(); + if (title.includes(searchTerm)) { + item.style.display = 'block'; + } else { + item.style.display = 'none'; + } + }); + } + + // Update word count + function updateWordCount() { + const text = entryContent.value; + const words = text.trim() ? text.trim().split(/\s+/).length : 0; + wordCount.textContent = `${words} words`; + } + + // Initialize the app + init(); +}); \ No newline at end of file diff --git a/MemoryDiary/style.css b/MemoryDiary/style.css new file mode 100644 index 000000000..a49875be2 --- /dev/null +++ b/MemoryDiary/style.css @@ -0,0 +1,549 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Poppins', sans-serif; + background: linear-gradient(135deg, #d9c7a7 0%, #e5d9c3 100%); + color: #5a3e2b; + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + padding: 20px; +} + +.book-cover { + width: 100%; + max-width: 1200px; + background: linear-gradient(to right, #c19a6b 0%, #b88a5a 100%); + border-radius: 15px; + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3), 0 0 0 10px #8b5a2b, 0 0 0 15px #a67b5b; + overflow: hidden; + position: relative; + padding: 20px; +} + +.book-binding { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 30px; + background: linear-gradient(to right, #8b5a2b 0%, #a67b5b 100%); + box-shadow: 5px 0 15px rgba(0, 0, 0, 0.2); +} + +.book-content { + margin-left: 30px; + background: #f8f4e9; + border-radius: 10px; + box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.1); + padding: 20px; + min-height: 650px; + position: relative; + overflow: hidden; +} + +.book-content::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/png;base64,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'); + opacity: 0.1; + pointer-events: none; +} + +header { + background: linear-gradient(to right, #8b5a2b, #a67b5b); + color: #f8f0dc; + padding: 20px; + text-align: center; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +header h1 { + font-family: 'Playfair Display', serif; + font-size: 2.5rem; + margin-bottom: 5px; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); +} + +.tagline { + font-style: italic; + opacity: 0.9; +} + +.auth-panel { + padding: 40px 20px; + display: flex; + justify-content: center; + align-items: center; +} + +.auth-container { + background: #f8f0dc; + padding: 30px; + border-radius: 15px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 450px; + text-align: center; + border: 1px solid #d9c7a7; +} + +.auth-container h2 { + margin-bottom: 20px; + color: #8b5a2b; +} + +.input-group { + margin-bottom: 20px; + text-align: left; +} + +.input-group label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: #5a3e2b; +} + +.input-group input { + width: 100%; + padding: 12px 15px; + border: 2px solid #d9c7a7; + border-radius: 8px; + font-size: 16px; + transition: border-color 0.3s; + background: #fffbf0; + color: #5a3e2b; +} + +.input-group input:focus { + border-color: #8b5a2b; + outline: none; +} + +.input-group small { + color: #8b5a2b; + font-size: 0.8rem; +} + +.btn-primary { + background: linear-gradient(to right, #8b5a2b, #a67b5b); + color: #f8f0dc; + border: none; + padding: 12px 25px; + border-radius: 8px; + font-size: 16px; + font-weight: 500; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(139, 90, 43, 0.4); +} + +.btn-cancel { + background: #f8f0dc; + color: #8b5a2b; + border: 1px solid #d9c7a7; + padding: 12px 25px; + border-radius: 8px; + font-size: 16px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s; +} + +.btn-cancel:hover { + background: #e5d9c3; +} + +.main-app { + display: flex; + min-height: 600px; +} + +.sidebar { + width: 300px; + background: #f8f0dc; + border-right: 1px solid #d9c7a7; + padding: 20px; + display: flex; + flex-direction: column; + border-radius: 8px 0 0 8px; +} + +.user-area { + text-align: center; + margin-bottom: 30px; +} + +.user-avatar { + width: 80px; + height: 80px; + background: linear-gradient(to right, #8b5a2b, #a67b5b); + border-radius: 50%; + margin: 0 auto 15px; + display: flex; + justify-content: center; + align-items: center; + color: #f8f0dc; + font-size: 2rem; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.user-greeting { + font-weight: 500; + color: #8b5a2b; +} + +.entry-list h3 { + margin-bottom: 15px; + color: #8b5a2b; + border-bottom: 2px solid #d9c7a7; + padding-bottom: 8px; +} + +.search-box { + position: relative; + margin-bottom: 20px; +} + +.search-box input { + width: 100%; + padding: 10px 15px 10px 40px; + border: 1px solid #d9c7a7; + border-radius: 8px; + font-size: 14px; + background: #fffbf0; + color: #5a3e2b; +} + +.search-box i { + position: absolute; + left: 15px; + top: 50%; + transform: translateY(-50%); + color: #8b5a2b; +} + +.entries { + flex-grow: 1; + overflow-y: auto; +} + +.entry-item { + padding: 12px 15px; + border-radius: 8px; + margin-bottom: 10px; + cursor: pointer; + transition: background 0.2s; + background: #fffbf0; + border: 1px solid #d9c7a7; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.entry-item:hover { + background: #f8e9c9; + border-color: #c19a6b; +} + +.entry-item.active { + background: #f8e9c9; + border-left: 4px solid #8b5a2b; +} + +.entry-title { + font-weight: 500; + margin-bottom: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: #5a3e2b; +} + +.entry-date { + font-size: 0.8rem; + color: #8b5a2b; +} + +.entry-preview { + font-size: 0.85rem; + color: #8b5a2b; + margin-top: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.btn-new-entry { + background: #8b5a2b; + color: #f8f0dc; + border: none; + padding: 12px; + border-radius: 8px; + margin-top: 20px; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + transition: background 0.2s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.btn-new-entry:hover { + background: #74502a; +} + +.editor-container { + flex-grow: 1; + display: flex; + flex-direction: column; + background: #f8f0dc; + border-radius: 0 8px 8px 0; + overflow: hidden; +} + +.page-texture { + background: #f8f0dc; + padding: 20px; + flex-grow: 1; + display: flex; + flex-direction: column; + position: relative; +} + +.page-texture::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/png;base64,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'); + opacity: 0.1; + pointer-events: none; +} + +.editor-header { + padding: 20px; + border-bottom: 1px solid #d9c7a7; + display: flex; + justify-content: space-between; + align-items: center; +} + +#entryTitle { + border: none; + font-size: 1.5rem; + font-weight: 600; + width: 70%; + color: #5a3e2b; + padding: 5px 0; + background: transparent; + font-family: 'Playfair Display', serif; +} + +#entryTitle:focus { + outline: none; + border-bottom: 2px solid #8b5a2b; +} + +.editor-actions { + display: flex; + gap: 10px; +} + +.btn-save { + background: #8b5a2b; + color: #f8f0dc; + border: none; + padding: 10px 15px; + border-radius: 6px; + cursor: pointer; + display: flex; + align-items: center; + gap: 5px; + transition: background 0.2s; +} + +.btn-save:hover { + background: #74502a; +} + +.btn-delete { + background: #a67b5b; + color: #f8f0dc; + border: none; + padding: 10px 15px; + border-radius: 6px; + cursor: pointer; + display: flex; + align-items: center; + gap: 5px; + transition: background 0.2s; +} + +.btn-delete:hover { + background: #8b5a2b; +} + +.editor-content { + flex-grow: 1; + padding: 20px; +} + +#entryContent { + width: 100%; + height: 100%; + border: none; + resize: none; + font-family: 'Poppins', sans-serif; + font-size: 16px; + line-height: 1.6; + padding: 10px; + border-radius: 8px; + background: transparent; + color: #5a3e2b; +} + +#entryContent:focus { + outline: none; + background: rgba(255, 255, 255, 0.5); + box-shadow: 0 0 0 2px rgba(139, 90, 43, 0.2); +} + +.editor-footer { + padding: 15px 20px; + border-top: 1px solid #d9c7a7; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.9rem; + color: #8b5a2b; +} + +.encryption-status { + display: flex; + align-items: center; + gap: 5px; +} + +.unlock-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: #f8f0dc; + padding: 30px; + border-radius: 15px; + box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2); + width: 100%; + max-width: 400px; + border: 2px solid #8b5a2b; +} + +.modal-content h3 { + margin-bottom: 20px; + color: #8b5a2b; + display: flex; + align-items: center; + gap: 10px; +} + +.modal-buttons { + display: flex; + gap: 10px; + margin-top: 20px; +} + +.modal-buttons button { + flex: 1; +} + +.hidden { + display: none !important; +} + +.notification { + position: fixed; + bottom: 20px; + right: 20px; + background: #8b5a2b; + color: #f8f0dc; + padding: 15px 20px; + border-radius: 8px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + transform: translateY(100px); + opacity: 0; + transition: transform 0.3s, opacity 0.3s; + z-index: 1001; +} + +.notification.show { + transform: translateY(0); + opacity: 1; +} + +/* Responsive design */ +@media (max-width: 768px) { + .main-app { + flex-direction: column; + } + + .sidebar { + width: 100%; + border-right: none; + border-bottom: 1px solid #d9c7a7; + } + + .editor-header { + flex-direction: column; + align-items: flex-start; + gap: 15px; + } + + #entryTitle { + width: 100%; + } + + .editor-actions { + width: 100%; + justify-content: flex-end; + } + + .book-cover { + padding: 10px; + } + + .book-content { + margin-left: 0; + padding: 15px; + } + + .book-binding { + display: none; + } +} \ No newline at end of file diff --git a/SkillMap/index (1).html b/SkillMap/index (1).html new file mode 100644 index 000000000..015355e1d --- /dev/null +++ b/SkillMap/index (1).html @@ -0,0 +1,185 @@ + + + + + + SkillMap - Interactive Learning Journey + + + + +
+ +
+
+ +

Welcome to SkillMap!

+

+ Embark on an interactive coding journey. Master programming skills + through engaging challenges and track your progress as you level up! +

+ +
+
+ + +
+
+ +

Your Learning Journey

+
XP: 0
+
+ +
+
+
+ +
+
+
+ +
+
HTML Basics
+
Learn the structure of web pages
+
+ +
+
+ +
+
CSS Styling
+
Make your websites beautiful
+
+ +
+
+ +
+
+ +
+
JavaScript
+
Add interactivity to your sites
+
+ +
+
+ +
+
+ +
+
Python
+
Versatile programming language
+
+ +
+
+ +
+
+ +
+
React
+
Build modern web applications
+
+ +
+
+ +
+
+ +
+
Node.js
+
Server-side JavaScript
+
+ +
+
+
+
+ + +
+
+
+
+

HTML Basics

+

Complete the challenge to earn XP

+
+
+ Question 1/3 +
+
+ +
+
+ What does HTML stand for? +
+
+
+ + + +
+ + + +
+
+
+ + +
+
+
+ +
+

Skill Completed!

+

+ Congratulations! You've mastered HTML Basics. Your dedication has paid + off! +

+
+50 XP Earned
+ +
+
+
+ + + + \ No newline at end of file diff --git a/SkillMap/script (1).js b/SkillMap/script (1).js new file mode 100644 index 000000000..8f1361989 --- /dev/null +++ b/SkillMap/script (1).js @@ -0,0 +1,349 @@ +// Game state +const gameState = { + currentPage: "landing-page", + currentSkill: null, + currentQuestion: 0, + userXP: 0, + completedSkills: [], + quizData: { + html: [ + { + type: "multiple-choice", + question: "What does HTML stand for?", + options: [ + "Hyper Text Markup Language", + "High Tech Modern Language", + "Hyper Transfer Markup Language", + "Home Tool Markup Language", + ], + correctAnswer: 0, + }, + { + type: "multiple-choice", + question: "Which tag is used to create a hyperlink?", + options: ["", "", "", ""], + correctAnswer: 1, + }, + { + type: "coding", + question: 'Create a simple HTML page with a heading that says "Hello, World!"', + starterCode: "\n\n\n My Page\n\n\n \n\n", + validate: function (code) { + return ( + code.includes("

Hello, World!

") || + code.includes("

Hello, World!

") || + code.includes("

Hello, World!

") + ); + } + } + ], + css: [ + { + type: "multiple-choice", + question: "What does CSS stand for?", + options: [ + "Computer Style Sheets", + "Creative Style System", + "Cascading Style Sheets", + "Colorful Style Sheets", + ], + correctAnswer: 2, + }, + { + type: "multiple-choice", + question: "Which property is used to change the background color?", + options: ["color", "bgcolor", "background-color", "background"], + correctAnswer: 2, + }, + { + type: "coding", + question: "Write CSS to make all paragraphs have blue text and 20px font size", + starterCode: "/* Write your CSS here */", + validate: function(code) { + return code.includes('color: blue') && + code.includes('font-size: 20px'); + } + } + ], + js: [ + { + type: "multiple-choice", + question: "Which of the following is a JavaScript data type?", + options: ["String", "Boolean", "Number", "All of the above"], + correctAnswer: 3, + }, + { + type: "coding", + question: "Write a function that returns the sum of two numbers", + starterCode: "function sum(a, b) {\n // Write your code here\n}", + validate: function(code) { + return code.includes('return') && + (code.includes('a + b') || code.includes('a+b')); + } + } + ], + python: [ + { + type: "multiple-choice", + question: "Which of these is used to define a function in Python?", + options: ["function", "def", "define", "func"], + correctAnswer: 1, + } + ], + react: [ + { + type: "multiple-choice", + question: "What is JSX?", + options: [ + "A JavaScript library", + "A syntax extension for JavaScript", + "A CSS framework", + "A database technology", + ], + correctAnswer: 1, + } + ], + node: [ + { + type: "multiple-choice", + question: "Node.js is built on which JavaScript engine?", + options: ["SpiderMonkey", "V8", "Chakra", "JavaScriptCore"], + correctAnswer: 1, + } + ] + }, + userAnswers: {} +}; + +// DOM Elements +const pages = { + landing: document.getElementById("landing-page"), + skillMap: document.getElementById("skill-map-page"), + learningLevel: document.getElementById("learning-level-page"), + completion: document.getElementById("completion-page") +}; + +// Navigation functions +function showPage(pageId) { + // Hide all pages + Object.values(pages).forEach(page => { + page.classList.remove("active"); + }); + + // Show the requested page + document.getElementById(pageId).classList.add("active"); + gameState.currentPage = pageId; + + // Update UI based on page + if (pageId === "skill-map-page") { + updateSkillMap(); + } +} + +// Initialize event listeners +function initializeEventListeners() { + // Start button + document.getElementById("start-btn").addEventListener("click", () => { + showPage("skill-map-page"); + }); + + // Back to landing button + document.getElementById("back-to-landing").addEventListener("click", () => { + showPage("landing-page"); + }); + + // Continue learning button + document.getElementById("continue-learning").addEventListener("click", () => { + showPage("skill-map-page"); + }); + + // Skill nodes + document.querySelectorAll(".skill-node").forEach(node => { + node.addEventListener("click", function() { + if (this.classList.contains("locked")) return; + + const skill = this.getAttribute("data-skill"); + gameState.currentSkill = skill; + gameState.currentQuestion = 0; + gameState.userAnswers[skill] = []; + + showPage("learning-level-page"); + loadQuestion(); + }); + }); + + // Quiz navigation + document.getElementById("next-question").addEventListener("click", nextQuestion); + document.getElementById("prev-question").addEventListener("click", prevQuestion); + document.getElementById("submit-quiz").addEventListener("click", submitQuiz); + document.getElementById("run-code").addEventListener("click", runCode); +} + +// Quiz functions +function loadQuestion() { + const skill = gameState.currentSkill; + const questionIndex = gameState.currentQuestion; + const questionData = gameState.quizData[skill][questionIndex]; + + // Update question counter + document.getElementById("current-question").textContent = questionIndex + 1; + document.getElementById("total-questions").textContent = gameState.quizData[skill].length; + document.getElementById("current-skill").textContent = + skill.charAt(0).toUpperCase() + skill.slice(1) + (skill === "html" ? " Basics" : ""); + + // Show/hide navigation buttons + document.getElementById("prev-question").style.display = + questionIndex > 0 ? "block" : "none"; + + document.getElementById("next-question").style.display = + questionIndex < gameState.quizData[skill].length - 1 ? "block" : "none"; + + document.getElementById("submit-quiz").style.display = + questionIndex === gameState.quizData[skill].length - 1 ? "block" : "none"; + + // Load question based on type + if (questionData.type === "multiple-choice") { + document.getElementById("code-editor").style.display = "none"; + document.getElementById("question-text").textContent = questionData.question; + + const optionsContainer = document.getElementById("options-container"); + optionsContainer.innerHTML = ""; + + questionData.options.forEach((option, index) => { + const optionElement = document.createElement("div"); + optionElement.className = "option"; + optionElement.textContent = option; + optionElement.addEventListener("click", () => selectOption(index)); + + // Check if already selected + if (gameState.userAnswers[skill][questionIndex] === index) { + optionElement.classList.add("selected"); + } + + optionsContainer.appendChild(optionElement); + }); + } else if (questionData.type === "coding") { + document.getElementById("code-editor").style.display = "block"; + document.getElementById("question-text").textContent = questionData.question; + document.getElementById("code-area").value = + gameState.userAnswers[skill][questionIndex] || questionData.starterCode; + } +} + +function selectOption(optionIndex) { + const skill = gameState.currentSkill; + const questionIndex = gameState.currentQuestion; + + // Update user answer + gameState.userAnswers[skill][questionIndex] = optionIndex; + + // Update UI + document.querySelectorAll(".option").forEach((option, index) => { + if (index === optionIndex) { + option.classList.add("selected"); + } else { + option.classList.remove("selected"); + } + }); +} + +function nextQuestion() { + gameState.currentQuestion++; + loadQuestion(); +} + +function prevQuestion() { + gameState.currentQuestion--; + loadQuestion(); +} + +function runCode() { + const skill = gameState.currentSkill; + const questionIndex = gameState.currentQuestion; + const code = document.getElementById("code-area").value; + + // Save code as answer + gameState.userAnswers[skill][questionIndex] = code; + + // Simple code execution simulation + const output = document.getElementById("output"); + output.textContent = "Running your code... (This is a simulation)"; + + // In a real app, you would execute the code safely + setTimeout(() => { + output.textContent = "Code executed successfully!"; + }, 1000); +} + +function submitQuiz() { + const skill = gameState.currentSkill; + let correctAnswers = 0; + + // Calculate score + gameState.quizData[skill].forEach((question, index) => { + if (question.type === "multiple-choice") { + if (gameState.userAnswers[skill][index] === question.correctAnswer) { + correctAnswers++; + } + } else if (question.type === "coding") { + if (question.validate(gameState.userAnswers[skill][index])) { + correctAnswers++; + } + } + }); + + // Calculate XP earned (50 XP per correct answer) + const earnedXP = correctAnswers * 50; + gameState.userXP += earnedXP; + + // Mark skill as completed + if (!gameState.completedSkills.includes(skill)) { + gameState.completedSkills.push(skill); + } + + // Update completion message + document.getElementById("completion-message").textContent = + `Congratulations! You've completed ${skill.toUpperCase()} Basics with ${correctAnswers} out of ${gameState.quizData[skill].length} correct answers.`; + document.getElementById("earned-xp").textContent = earnedXP; + + showPage("completion-page"); +} + +function updateSkillMap() { + // Update XP display + document.getElementById("xp-count").textContent = gameState.userXP; + + // Update progress bar + const totalSkills = document.querySelectorAll(".skill-node").length; + const completedSkills = gameState.completedSkills.length; + const progress = (completedSkills / totalSkills) * 100; + document.getElementById("progress-bar").style.width = `${progress}%`; + + // Update skill nodes + document.querySelectorAll(".skill-node").forEach(node => { + const skill = node.getAttribute("data-skill"); + + // Mark completed skills + if (gameState.completedSkills.includes(skill)) { + node.classList.add("completed"); + node.classList.remove("locked"); + } + + // Unlock next skill if previous is completed + const allSkills = Array.from(document.querySelectorAll(".skill-node")).map(n => n.getAttribute("data-skill")); + const currentIndex = allSkills.indexOf(skill); + + if (currentIndex > 0) { + const prevSkill = allSkills[currentIndex - 1]; + if (gameState.completedSkills.includes(prevSkill)) { + node.classList.remove("locked"); + } + } + }); +} + +// Initialize the app when DOM is loaded +document.addEventListener("DOMContentLoaded", function() { + initializeEventListeners(); + updateSkillMap(); +}); \ No newline at end of file diff --git a/SkillMap/style (1).css b/SkillMap/style (1).css new file mode 100644 index 000000000..33fdf821b --- /dev/null +++ b/SkillMap/style (1).css @@ -0,0 +1,355 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; +} + +body { + background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); + color: white; + min-height: 100vh; + overflow-x: hidden; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.page { + display: none; + min-height: 90vh; + padding: 20px; + border-radius: 15px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + animation: fadeIn 0.5s ease; + position: relative; + z-index: 1; +} + +.active { + display: block; +} + +.landing-page { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + position: relative; + z-index: 2; +} + +.logo { + font-size: 4rem; + margin-bottom: 20px; + color: #fdbb2d; + text-shadow: 0 0 10px rgba(253, 187, 45, 0.7); +} + +h1 { + font-size: 3.5rem; + margin-bottom: 20px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +.subtitle { + font-size: 1.5rem; + margin-bottom: 40px; + max-width: 600px; + line-height: 1.6; +} + +.btn { + padding: 15px 40px; + font-size: 1.2rem; + background: linear-gradient(to right, #fdbb2d, #b21f1f); + border: none; + border-radius: 50px; + color: white; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + font-weight: bold; + position: relative; + z-index: 10; +} + +.btn:hover { + transform: translateY(-5px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4); +} + +.skill-map-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; +} + +.back-btn { + background: rgba(255, 255, 255, 0.2); + border: none; + color: white; + padding: 10px 20px; + border-radius: 50px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s ease; +} + +.back-btn:hover { + background: rgba(255, 255, 255, 0.3); +} + +.progress-container { + width: 100%; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + margin-bottom: 30px; + overflow: hidden; +} + +.progress-bar { + height: 20px; + background: linear-gradient(to right, #fdbb2d, #b21f1f); + border-radius: 10px; + width: 0%; + transition: width 0.5s ease; +} + +.skill-map { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-top: 30px; +} + +.skill-node { + background: rgba(255, 255, 255, 0.15); + border-radius: 15px; + padding: 25px; + text-align: center; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + border: 2px solid transparent; +} + +.skill-node:hover { + transform: translateY(-10px); + background: rgba(255, 255, 255, 0.25); + border-color: #fdbb2d; +} + +.skill-node.completed { + background: rgba(76, 175, 80, 0.3); + border-color: #4caf50; +} + +.skill-node.locked { + opacity: 0.6; + cursor: not-allowed; +} + +.skill-node.locked:hover { + transform: none; + background: rgba(255, 255, 255, 0.15); + border-color: transparent; +} + +.skill-icon { + font-size: 3rem; + margin-bottom: 15px; +} + +.skill-name { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 10px; +} + +.skill-desc { + font-size: 0.9rem; + opacity: 0.8; +} + +.lock-icon { + position: absolute; + top: 10px; + right: 10px; + color: #fdbb2d; +} + +.quiz-container { + max-width: 800px; + margin: 0 auto; +} + +.quiz-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; +} + +.quiz-title { + font-size: 2rem; + margin-bottom: 10px; +} + +.quiz-progress { + font-size: 1.2rem; + background: rgba(255, 255, 255, 0.2); + padding: 10px 20px; + border-radius: 50px; +} + +.question-container { + background: rgba(255, 255, 255, 0.15); + padding: 30px; + border-radius: 15px; + margin-bottom: 30px; +} + +.question { + font-size: 1.5rem; + margin-bottom: 20px; +} + +.options { + display: grid; + gap: 15px; +} + +.option { + background: rgba(255, 255, 255, 0.1); + padding: 15px; + border-radius: 10px; + cursor: pointer; + transition: all 0.3s ease; +} + +.option:hover { + background: rgba(255, 255, 255, 0.2); +} + +.option.selected { + background: rgba(253, 187, 45, 0.3); + border: 2px solid #fdbb2d; +} + +.code-editor { + background: #1e1e1e; + border-radius: 10px; + padding: 20px; + margin-bottom: 30px; + font-family: "Courier New", monospace; +} + +.code-header { + display: flex; + justify-content: space-between; + margin-bottom: 15px; +} + +.code-title { + font-size: 1.2rem; +} + +.code-area { + width: 100%; + min-height: 200px; + background: #1e1e1e; + color: #d4d4d4; + border: none; + padding: 15px; + border-radius: 5px; + font-family: "Courier New", monospace; + font-size: 1rem; + resize: vertical; +} + +.quiz-actions { + display: flex; + justify-content: space-between; + margin-top: 30px; +} + +.completion-page { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + +.completion-icon { + font-size: 8rem; + color: #4caf50; + margin-bottom: 30px; + animation: bounce 1s infinite alternate; +} + +.completion-title { + font-size: 3rem; + margin-bottom: 20px; +} + +.completion-message { + font-size: 1.5rem; + margin-bottom: 40px; + max-width: 600px; +} + +.xp-earned { + background: rgba(255, 255, 255, 0.2); + padding: 15px 30px; + border-radius: 50px; + font-size: 1.2rem; + margin-bottom: 40px; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes bounce { + from { + transform: translateY(0); + } + to { + transform: translateY(-20px); + } +} + +@media (max-width: 768px) { + h1 { + font-size: 2.5rem; + } + + .skill-map { + grid-template-columns: 1fr; + } + + .quiz-header { + flex-direction: column; + gap: 15px; + align-items: flex-start; + } +} \ No newline at end of file