diff --git a/app.js b/app.js
index 6efb32c..219f526 100644
--- a/app.js
+++ b/app.js
@@ -1,15 +1,13 @@
-// Application data from the provided JSON
-const subjects = {
+const SUBJECT_CATALOG = {
"Engineering Mathematics-III": {
credits: 3,
chapters: [
"Laplace Transforms",
- "Fourier Series",
+ "Fourier Series",
"Partial Differential Equations",
"Z-Transforms",
"Functions of Complex Variables"
],
-
totalChapters: 5
},
"Data Structures": {
@@ -17,7 +15,7 @@ const subjects = {
chapters: [
"Data, Data types, Arrays and Hash Tables",
"Stacks and Queues",
- "Linked Lists",
+ "Linked Lists",
"Trees and Graphs",
"Searching and Sorting"
],
@@ -29,7 +27,7 @@ const subjects = {
"Propositional Logic and Predicates",
"Set Theory, Functions and Relations",
"Combinatorics",
- "Graph Theory and Trees",
+ "Graph Theory and Trees",
"Algebraic Structures"
],
totalChapters: 5
@@ -68,200 +66,187 @@ const subjects = {
}
};
-const gradeScale = {
- "EX": {min: 91, points: 10},
- "AA`": {min: 86, points: 9},
- "AB": {min: 81, points: 8},
- "BB": {min: 76, points: 7},
- "BC": {min: 71, points: 6},
- "CC": {min: 66, points: 5},
- "CD": {min: 61, points: 4},
- "DD": {min: 56, points: 0},
- "DE": {min: 51, points: 0},
- "EE": {min: 40, points: 0},
- "EF": {min: 0, points: 0},
+const GRADE_SCALE = {
+ "EX": { min: 91, points: 10 },
+ "AA": { min: 86, points: 9 },
+ "AB": { min: 81, points: 8 },
+ "BB": { min: 76, points: 7 },
+ "BC": { min: 71, points: 6 },
+ "CC": { min: 66, points: 5 },
+ "CD": { min: 61, points: 4 },
+ "DD": { min: 56, points: 0 },
+ "DE": { min: 51, points: 0 },
+ "EE": { min: 40, points: 0 },
+ "EF": { min: 0, points: 0 }
};
-// Global state
let studyPlan = [];
-let folderStructure = {};
-let subjectMarks = {};
-let studyProgress = {};
+let materialFolders = {};
+let subjectMarksByName = {};
+let subjectChapterProgress = {};
-// Initialize application
-document.addEventListener('DOMContentLoaded', function() {
- console.log('App initializing...');
+document.addEventListener('DOMContentLoaded', function () {
initializeApp();
setupEventListeners();
- populateSubjectSelectors();
- initializeFolderStructure();
- calculateModeDurations();
+ populateSubjectDropdowns();
+ initializeMaterialFolders();
+ renderModeDurations();
});
function initializeApp() {
- console.log('Setting up initial data...');
- // Set default dates
const today = new Date();
const startDate = new Date(today);
- startDate.setDate(today.getDate() + 7); // Start next week
-
+ startDate.setDate(today.getDate() + 7);
+
const examDate = new Date(today);
- examDate.setDate(today.getDate() + 90); // Exam in 3 months
-
+ examDate.setDate(today.getDate() + 90);
+
const startDateInput = document.getElementById('startDate');
const examDateInput = document.getElementById('examDate');
-
+
if (startDateInput && examDateInput) {
startDateInput.value = startDate.toISOString().split('T')[0];
examDateInput.value = examDate.toISOString().split('T')[0];
}
-
- // Initialize subject marks
- Object.keys(subjects).forEach(subject => {
- subjectMarks[subject] = {
- ct1: 0, ct2: 0, assignment: 0, midSem: 0, endSem: 0
+
+ Object.keys(SUBJECT_CATALOG).forEach(subjectName => {
+ subjectMarksByName[subjectName] = {
+ ct1: 0,
+ ct2: 0,
+ assignment: 0,
+ midSem: 0,
+ endSem: 0
};
- studyProgress[subject] = {};
- subjects[subject].chapters.forEach((chapter, index) => {
- studyProgress[subject][index] = false;
+ subjectChapterProgress[subjectName] = {};
+ SUBJECT_CATALOG[subjectName].chapters.forEach((_, index) => {
+ subjectChapterProgress[subjectName][index] = false;
});
});
}
function setupEventListeners() {
- console.log('Setting up event listeners...');
-
- // Tab navigation - Fixed implementation
document.querySelectorAll('.nav-tab').forEach(tab => {
- tab.addEventListener('click', function(e) {
- e.preventDefault();
- const targetTab = this.dataset.tab;
- console.log('Tab clicked:', targetTab);
- switchTab(targetTab);
+ tab.addEventListener('click', event => {
+ event.preventDefault();
+ switchTab(tab.dataset.tab);
});
});
-
- // Study plan generator
+
const generateBtn = document.getElementById('generatePlan');
if (generateBtn) {
- generateBtn.addEventListener('click', function(e) {
- e.preventDefault();
- console.log('Generate plan clicked');
- generateStudyPlan();
+ generateBtn.addEventListener('click', event => {
+ event.preventDefault();
+ handleGeneratePlanClick();
});
}
-
- // Folder manager
+
const addMaterialBtn = document.getElementById('addMaterial');
if (addMaterialBtn) {
- addMaterialBtn.addEventListener('click', function(e) {
- e.preventDefault();
- showAddMaterialModal();
+ addMaterialBtn.addEventListener('click', event => {
+ event.preventDefault();
+ openMaterialModal();
});
}
-
+
const searchInput = document.getElementById('searchMaterials');
if (searchInput) {
- searchInput.addEventListener('input', searchMaterials);
+ searchInput.addEventListener('input', filterMaterials);
}
-
- // CGPA calculator
+
const calculateBtn = document.getElementById('calculateCGPA');
if (calculateBtn) {
- calculateBtn.addEventListener('click', function(e) {
- e.preventDefault();
- console.log('Calculate CGPA clicked');
- calculateCGPA();
+ calculateBtn.addEventListener('click', event => {
+ event.preventDefault();
+ handleCalculateCgpaClick();
});
}
-
+
const subjectSelect = document.getElementById('subjectSelect');
if (subjectSelect) {
- subjectSelect.addEventListener('change', loadSubjectMarks);
- }
-
- // Performance modes
- document.querySelectorAll('.mode-select').forEach(btn => {
- btn.addEventListener('click', function(e) {
- e.preventDefault();
- const mode = this.dataset.mode;
- console.log('Mode selected:', mode);
- selectPerformanceMode(mode);
+ subjectSelect.addEventListener('change', loadMarksForSelectedSubject);
+ }
+
+ document.querySelectorAll('.mode-select').forEach(button => {
+ button.addEventListener('click', event => {
+ event.preventDefault();
+ handleModeSelection(button.dataset.mode);
});
});
-
- // Modal controls
+
const cancelBtn = document.getElementById('cancelMaterial');
const saveBtn = document.getElementById('saveMaterial');
const closeBtn = document.querySelector('.modal-close');
-
- if (cancelBtn) cancelBtn.addEventListener('click', function(e) { e.preventDefault(); hideAddMaterialModal(); });
- if (saveBtn) saveBtn.addEventListener('click', function(e) { e.preventDefault(); saveMaterial(); });
- if (closeBtn) closeBtn.addEventListener('click', function(e) { e.preventDefault(); hideAddMaterialModal(); });
-
- // Material subject change
+
+ if (cancelBtn) {
+ cancelBtn.addEventListener('click', event => {
+ event.preventDefault();
+ closeMaterialModal();
+ });
+ }
+
+ if (saveBtn) {
+ saveBtn.addEventListener('click', event => {
+ event.preventDefault();
+ saveNewMaterial();
+ });
+ }
+
+ if (closeBtn) {
+ closeBtn.addEventListener('click', event => {
+ event.preventDefault();
+ closeMaterialModal();
+ });
+ }
+
const materialSubject = document.getElementById('materialSubject');
if (materialSubject) {
- materialSubject.addEventListener('change', updateMaterialChapters);
+ materialSubject.addEventListener('change', populateMaterialChapterOptions);
}
}
function switchTab(tabId) {
- console.log('Switching to tab:', tabId);
-
- // Update tab buttons
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.classList.remove('active');
});
+
const activeTab = document.querySelector(`[data-tab="${tabId}"]`);
if (activeTab) {
activeTab.classList.add('active');
}
-
- // Update content - Hide all first
+
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
content.style.display = 'none';
});
-
- // Show selected content
+
const targetContent = document.getElementById(tabId);
if (targetContent) {
targetContent.classList.add('active');
targetContent.style.display = 'block';
- console.log('Tab switched successfully to:', tabId);
- } else {
- console.error('Target content not found:', tabId);
}
}
-// SECTION 1: Study Plan Generator
-function generateStudyPlan() {
- console.log('Generating study plan...');
-
+function handleGeneratePlanClick() {
const modeSelect = document.getElementById('studyMode');
const startDateInput = document.getElementById('startDate');
const examDateInput = document.getElementById('examDate');
-
+
if (!modeSelect || !startDateInput || !examDateInput) {
- console.error('Required elements not found');
return;
}
-
+
const mode = modeSelect.value;
const startDate = new Date(startDateInput.value);
const examDate = new Date(examDateInput.value);
-
- console.log('Mode:', mode, 'Start:', startDate, 'Exam:', examDate);
-
+
if (!startDateInput.value || !examDateInput.value || examDate <= startDate) {
alert('Please select valid start and exam dates');
return;
}
-
- studyPlan = createStudyPlan(mode, startDate, examDate);
- displayStudyPlan();
-
+
+ studyPlan = buildStudyPlan(mode, startDate, examDate);
+ renderStudyPlan();
+
const output = document.getElementById('studyPlanOutput');
if (output) {
output.classList.remove('hidden');
@@ -269,29 +254,27 @@ function generateStudyPlan() {
}
}
-function createStudyPlan(mode, startDate, examDate) {
- console.log('Creating study plan for mode:', mode);
+function buildStudyPlan(mode, startDate, examDate) {
const plan = [];
- const subjectList = Object.entries(subjects).sort((a, b) => b[1].credits - a[1].credits);
-
+ const subjectList = Object.entries(SUBJECT_CATALOG).sort((a, b) => b[1].credits - a[1].credits);
+
if (mode === 'intense') {
- // 7-day crash course
const intenseDays = 7;
const totalChapters = subjectList.reduce((sum, [, subject]) => sum + subject.totalChapters, 0);
const chaptersPerDay = Math.ceil(totalChapters / intenseDays);
-
+
let currentDate = new Date(examDate);
currentDate.setDate(currentDate.getDate() - intenseDays);
-
+
let chapterIndex = 0;
let currentSubjectIndex = 0;
-
+
for (let day = 0; day < intenseDays; day++) {
const dayPlan = {
date: new Date(currentDate),
subjects: []
};
-
+
let chaptersToday = 0;
while (chaptersToday < chaptersPerDay && currentSubjectIndex < subjectList.length) {
const [subjectName, subject] = subjectList[currentSubjectIndex];
@@ -304,400 +287,414 @@ function createStudyPlan(mode, startDate, examDate) {
chapterIndex++;
chaptersToday++;
}
-
+
if (chapterIndex >= subject.chapters.length) {
currentSubjectIndex++;
chapterIndex = 0;
}
}
-
+
if (dayPlan.subjects.length > 0) {
plan.push(dayPlan);
}
-
+
currentDate.setDate(currentDate.getDate() + 1);
}
} else {
- // Easy or Normal mode
const chaptersPerDay = mode === 'easy' ? 1 : 2;
const daysBetween = Math.ceil((examDate - startDate) / (1000 * 60 * 60 * 24));
-
let currentDate = new Date(startDate);
let allChapters = [];
-
- // Create weighted chapter list
+
subjectList.forEach(([subjectName, subject]) => {
- subject.chapters.forEach(chapter => {
+ subject.chapters.forEach(chapterName => {
allChapters.push({
subject: subjectName,
- chapter: chapter,
+ chapter: chapterName,
priority: subject.credits >= 3 ? 'high' : subject.credits === 2 ? 'medium' : 'low'
});
});
});
-
- // Distribute chapters across available days
+
for (let day = 0; day < daysBetween && allChapters.length > 0; day++) {
const dayPlan = {
date: new Date(currentDate),
subjects: []
};
-
+
for (let i = 0; i < chaptersPerDay && allChapters.length > 0; i++) {
dayPlan.subjects.push(allChapters.shift());
}
-
+
if (dayPlan.subjects.length > 0) {
plan.push(dayPlan);
}
-
+
currentDate.setDate(currentDate.getDate() + 1);
}
}
-
+
return plan;
}
-function displayStudyPlan() {
+function renderStudyPlan() {
const planDetails = document.getElementById('planDetails');
if (!planDetails) return;
-
+
planDetails.innerHTML = '';
-
- studyPlan.forEach((day, index) => {
+
+ studyPlan.forEach((day, dayIndex) => {
const dayElement = document.createElement('div');
dayElement.className = 'plan-day';
- dayElement.innerHTML = `
-
+
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = `day-${dayIndex}`;
+ checkbox.addEventListener('change', () => toggleDayCompletion(dayIndex));
+
+ const subjectsHtml = day.subjects.map(item => `
+ ${escapeHtml(item.subject)}
+ ${escapeHtml(item.chapter)}
+ `).join(' • ');
+
+ dayElement.appendChild(checkbox);
+ dayElement.insertAdjacentHTML('beforeend', `
-
${day.date.toLocaleDateString('en-US', {
- weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
+
${day.date.toLocaleDateString('en-US', {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
})}
-
- ${day.subjects.map(s => `
- ${s.subject}
- ${s.chapter}
- `).join(' • ')}
-
+
${subjectsHtml}
- `;
+ `);
+
planDetails.appendChild(dayElement);
});
-
- updateProgressDisplay();
+
+ renderOverallProgress();
}
-function updateProgress(dayIndex) {
+function toggleDayCompletion(dayIndex) {
const checkbox = document.getElementById(`day-${dayIndex}`);
- const dayElement = checkbox.closest('.plan-day');
-
+ const dayElement = checkbox && checkbox.closest('.plan-day');
+ if (!dayElement) return;
+
if (checkbox.checked) {
dayElement.classList.add('completed');
} else {
dayElement.classList.remove('completed');
}
-
- updateProgressDisplay();
+
+ renderOverallProgress();
}
-function updateProgressDisplay() {
+function renderOverallProgress() {
const completedDays = document.querySelectorAll('.plan-day input:checked').length;
const totalDays = studyPlan.length;
const progress = totalDays > 0 ? (completedDays / totalDays) * 100 : 0;
-
+
const progressFill = document.getElementById('overallProgress');
const progressText = document.getElementById('progressText');
-
+
if (progressFill) progressFill.style.width = `${progress}%`;
if (progressText) progressText.textContent = `${Math.round(progress)}% Complete`;
}
-// SECTION 2: Folder Manager
-function initializeFolderStructure() {
- folderStructure = {};
- Object.keys(subjects).forEach(subjectName => {
- folderStructure[subjectName] = {
+function initializeMaterialFolders() {
+ materialFolders = {};
+
+ Object.keys(SUBJECT_CATALOG).forEach(subjectName => {
+ materialFolders[subjectName] = {
chapters: {},
materials: []
};
-
- subjects[subjectName].chapters.forEach((chapter, index) => {
- folderStructure[subjectName].chapters[index] = {
- name: chapter,
+
+ SUBJECT_CATALOG[subjectName].chapters.forEach((chapterName, index) => {
+ materialFolders[subjectName].chapters[index] = {
+ name: chapterName,
materials: []
};
});
});
-
- renderFolderStructure();
+
+ renderMaterialFolders();
}
-function renderFolderStructure() {
+function renderMaterialFolders() {
const container = document.getElementById('folderStructure');
if (!container) return;
-
+
container.innerHTML = '';
-
- Object.entries(folderStructure).forEach(([subjectName, subjectData]) => {
+
+ Object.entries(materialFolders).forEach(([subjectName, subjectData]) => {
+ const totalMaterials = subjectData.materials.length +
+ Object.values(subjectData.chapters).reduce((sum, chapter) => sum + chapter.materials.length, 0);
+
const subjectFolder = document.createElement('div');
subjectFolder.className = 'folder-item';
-
- const totalMaterials = subjectData.materials.length +
- Object.values(subjectData.chapters).reduce((sum, chapter) => sum + chapter.materials.length, 0);
-
- subjectFolder.innerHTML = `
-
-
- ${Object.entries(subjectData.chapters).map(([chapterIndex, chapter]) => `
-
-
-
- ${chapter.materials.map((material, materialIndex) => `
-
-
${material.type}
-
${material.name}
-
-
-
-
- `).join('')}
-
-
- `).join('')}
- ${subjectData.materials.map((material, materialIndex) => `
-
-
${material.type}
-
${material.name}
-
-
-
-
- `).join('')}
-
+
+ const subjectHeader = document.createElement('div');
+ subjectHeader.className = 'folder-header';
+ subjectHeader.innerHTML = `
+
📁
+
${escapeHtml(subjectName)}
+
(${totalMaterials} materials)
`;
-
+
+ const subjectChildrenId = `folder-${sanitizeHtmlId(subjectName)}`;
+ const subjectChildren = document.createElement('div');
+ subjectChildren.className = 'folder-children';
+ subjectChildren.id = subjectChildrenId;
+
+ subjectHeader.addEventListener('click', () => toggleSubjectFolder(subjectName));
+
+ Object.entries(subjectData.chapters).forEach(([chapterIndex, chapter]) => {
+ const chapterFolder = document.createElement('div');
+ chapterFolder.className = 'folder-item';
+
+ const chapterHeader = document.createElement('div');
+ chapterHeader.className = 'folder-header';
+ chapterHeader.innerHTML = `
+
📄
+
${escapeHtml(chapter.name)}
+
(${chapter.materials.length} materials)
+ `;
+
+ const chapterChildrenId = `chapter-${sanitizeHtmlId(subjectName)}-${chapterIndex}`;
+ const chapterChildren = document.createElement('div');
+ chapterChildren.className = 'folder-children';
+ chapterChildren.id = chapterChildrenId;
+
+ chapterHeader.addEventListener('click', () => toggleChapterFolder(subjectName, chapterIndex));
+
+ chapter.materials.forEach((material, materialIndex) => {
+ chapterChildren.appendChild(createMaterialElement(subjectName, chapterIndex, materialIndex, material));
+ });
+
+ chapterFolder.appendChild(chapterHeader);
+ chapterFolder.appendChild(chapterChildren);
+ subjectChildren.appendChild(chapterFolder);
+ });
+
+ subjectData.materials.forEach((material, materialIndex) => {
+ subjectChildren.appendChild(createMaterialElement(subjectName, null, materialIndex, material));
+ });
+
+ subjectFolder.appendChild(subjectHeader);
+ subjectFolder.appendChild(subjectChildren);
container.appendChild(subjectFolder);
});
}
-function toggleFolder(subjectName) {
- const folder = document.getElementById(`folder-${subjectName}`);
- if (folder) {
- folder.style.display = folder.style.display === 'none' ? 'block' : 'none';
- }
+function createMaterialElement(subjectName, chapterIndex, materialIndex, material) {
+ const element = document.createElement('div');
+ element.className = 'material-item';
+ element.innerHTML = `
+
${escapeHtml(material.type)}
+
${escapeHtml(material.name)}
+
+
+
+ `;
+
+ const removeButton = element.querySelector('.remove-material-btn');
+ removeButton.addEventListener('click', () => deleteMaterial(subjectName, chapterIndex, materialIndex));
+
+ return element;
}
-function toggleChapter(subjectName, chapterIndex) {
- const chapter = document.getElementById(`chapter-${subjectName}-${chapterIndex}`);
- if (chapter) {
- chapter.style.display = chapter.style.display === 'none' ? 'block' : 'none';
- }
+function toggleSubjectFolder(subjectName) {
+ const folder = document.getElementById(`folder-${sanitizeHtmlId(subjectName)}`);
+ if (!folder) return;
+
+ folder.style.display = folder.style.display === 'none' ? 'block' : 'none';
+}
+
+function toggleChapterFolder(subjectName, chapterIndex) {
+ const chapter = document.getElementById(`chapter-${sanitizeHtmlId(subjectName)}-${chapterIndex}`);
+ if (!chapter) return;
+
+ chapter.style.display = chapter.style.display === 'none' ? 'block' : 'none';
}
-function showAddMaterialModal() {
+function openMaterialModal() {
const modal = document.getElementById('addMaterialModal');
if (modal) {
modal.classList.remove('hidden');
}
}
-function hideAddMaterialModal() {
+function closeMaterialModal() {
const modal = document.getElementById('addMaterialModal');
if (modal) {
modal.classList.add('hidden');
}
- // Clear form
+
const materialName = document.getElementById('materialName');
const materialType = document.getElementById('materialType');
if (materialName) materialName.value = '';
if (materialType) materialType.value = 'pdf';
}
-function updateMaterialChapters() {
+function populateMaterialChapterOptions() {
const subjectSelect = document.getElementById('materialSubject');
const chapterSelect = document.getElementById('materialChapter');
-
+
if (!subjectSelect || !chapterSelect) return;
-
+
const selectedSubject = subjectSelect.value;
-
chapterSelect.innerHTML = '
';
-
- if (selectedSubject && subjects[selectedSubject]) {
- subjects[selectedSubject].chapters.forEach((chapter, index) => {
+
+ if (selectedSubject && SUBJECT_CATALOG[selectedSubject]) {
+ SUBJECT_CATALOG[selectedSubject].chapters.forEach((chapterName, index) => {
const option = document.createElement('option');
option.value = index;
- option.textContent = chapter;
+ option.textContent = chapterName;
chapterSelect.appendChild(option);
});
}
}
-function saveMaterial() {
- const subjectName = document.getElementById('materialSubject').value;
- const chapterIndex = document.getElementById('materialChapter').value;
- const materialName = document.getElementById('materialName').value;
- const materialType = document.getElementById('materialType').value;
-
+function saveNewMaterial() {
+ const subjectSelect = document.getElementById('materialSubject');
+ const chapterSelect = document.getElementById('materialChapter');
+ const nameInput = document.getElementById('materialName');
+ const typeInput = document.getElementById('materialType');
+
+ const subjectName = subjectSelect ? subjectSelect.value : '';
+ const chapterIndex = chapterSelect ? chapterSelect.value : '';
+ const materialName = nameInput ? nameInput.value.trim() : '';
+ const materialType = typeInput ? typeInput.value : 'pdf';
+
if (!subjectName || !materialName) {
alert('Please fill in all required fields');
return;
}
-
+
const material = {
name: materialName,
type: materialType,
dateAdded: new Date().toLocaleDateString()
};
-
+
if (chapterIndex !== '') {
- folderStructure[subjectName].chapters[chapterIndex].materials.push(material);
+ materialFolders[subjectName].chapters[chapterIndex].materials.push(material);
} else {
- folderStructure[subjectName].materials.push(material);
+ materialFolders[subjectName].materials.push(material);
}
-
- renderFolderStructure();
- hideAddMaterialModal();
+
+ renderMaterialFolders();
+ closeMaterialModal();
}
-function removeMaterial(subjectName, chapterIndex, materialIndex) {
- if (chapterIndex !== null) {
- folderStructure[subjectName].chapters[chapterIndex].materials.splice(materialIndex, 1);
+function deleteMaterial(subjectName, chapterIndex, materialIndex) {
+ if (chapterIndex !== null && chapterIndex !== undefined) {
+ materialFolders[subjectName].chapters[chapterIndex].materials.splice(materialIndex, 1);
} else {
- folderStructure[subjectName].materials.splice(materialIndex, 1);
+ materialFolders[subjectName].materials.splice(materialIndex, 1);
}
-
- renderFolderStructure();
+
+ renderMaterialFolders();
}
-function searchMaterials() {
- const searchTerm = document.getElementById('searchMaterials').value.toLowerCase();
- const materials = document.querySelectorAll('.material-item');
-
- materials.forEach(material => {
- const materialName = material.querySelector('.material-name');
- if (materialName && materialName.textContent.toLowerCase().includes(searchTerm)) {
- material.style.display = 'flex';
- } else {
- material.style.display = 'none';
- }
+function filterMaterials() {
+ const searchInput = document.getElementById('searchMaterials');
+ const searchTerm = (searchInput && searchInput.value || '').toLowerCase();
+
+ document.querySelectorAll('.material-item').forEach(item => {
+ const nameElement = item.querySelector('.material-name');
+ const materialName = nameElement ? nameElement.textContent.toLowerCase() : '';
+
+ item.style.display = materialName.includes(searchTerm) ? 'flex' : 'none';
});
}
-// SECTION 3: CGPA Calculator
-function populateSubjectSelectors() {
+function populateSubjectDropdowns() {
const subjectSelect = document.getElementById('subjectSelect');
const materialSubjectSelect = document.getElementById('materialSubject');
-
+
if (subjectSelect) {
- Object.keys(subjects).forEach(subject => {
+ Object.keys(SUBJECT_CATALOG).forEach(subjectName => {
const option = document.createElement('option');
- option.value = subject;
- option.textContent = subject;
+ option.value = subjectName;
+ option.textContent = subjectName;
subjectSelect.appendChild(option);
});
-
- // Load first subject by default
- if (Object.keys(subjects).length > 0) {
- subjectSelect.value = Object.keys(subjects)[0];
- loadSubjectMarks();
+
+ const firstSubject = Object.keys(SUBJECT_CATALOG)[0];
+ if (firstSubject) {
+ subjectSelect.value = firstSubject;
+ loadMarksForSelectedSubject();
}
}
-
+
if (materialSubjectSelect) {
- Object.keys(subjects).forEach(subject => {
+ Object.keys(SUBJECT_CATALOG).forEach(subjectName => {
const option = document.createElement('option');
- option.value = subject;
- option.textContent = subject;
+ option.value = subjectName;
+ option.textContent = subjectName;
materialSubjectSelect.appendChild(option);
});
}
}
-function loadSubjectMarks() {
+function loadMarksForSelectedSubject() {
const subjectSelect = document.getElementById('subjectSelect');
if (!subjectSelect) return;
-
- const subject = subjectSelect.value;
- const marks = subjectMarks[subject];
-
- if (marks) {
- const ct1 = document.getElementById('ct1');
- const ct2 = document.getElementById('ct2');
- const assignment = document.getElementById('assignment');
- const midSem = document.getElementById('midSem');
- const endSem = document.getElementById('endSem');
-
- if (ct1) ct1.value = marks.ct1;
- if (ct2) ct2.value = marks.ct2;
- if (assignment) assignment.value = marks.assignment;
- if (midSem) midSem.value = marks.midSem;
- if (endSem) endSem.value = marks.endSem;
- }
+
+ const marks = subjectMarksByName[subjectSelect.value];
+ if (!marks) return;
+
+ const fieldMap = {
+ ct1: 'ct1',
+ ct2: 'ct2',
+ assignment: 'assignment',
+ midSem: 'midSem',
+ endSem: 'endSem'
+ };
+
+ Object.entries(fieldMap).forEach(([key, id]) => {
+ const input = document.getElementById(id);
+ if (input) input.value = marks[key];
+ });
}
-function calculateCGPA() {
+function handleCalculateCgpaClick() {
const subjectSelect = document.getElementById('subjectSelect');
if (!subjectSelect) return;
-
+
const subject = subjectSelect.value;
- const ct1 = parseFloat(document.getElementById('ct1').value) || 0;
- const ct2 = parseFloat(document.getElementById('ct2').value) || 0;
- const assignment = parseFloat(document.getElementById('assignment').value) || 0;
- const midSem = parseFloat(document.getElementById('midSem').value) || 0;
- const endSem = parseFloat(document.getElementById('endSem').value) || 0;
-
- // Save marks
- subjectMarks[subject] = { ct1, ct2, assignment, midSem, endSem };
-
- // Calculate best 2 of 3 from CT1, CT2, Assignment
- const caScores = [ct1, ct2, assignment].sort((a, b) => b - a);
- const caTotal = caScores[0] + caScores[1]; // Best 2 scores
-
- const totalScore = caTotal + midSem + endSem;
- const percentage = (totalScore / 100) * 100;
-
- // Determine grade
- let grade = 'F';
- let gradePoints = 0;
-
- for (const [gradeName, gradeData] of Object.entries(gradeScale)) {
- if (percentage >= gradeData.min) {
- grade = gradeName;
- gradePoints = gradeData.points;
- break;
- }
- }
-
- // Calculate required marks for 9.0 CGPA (A grade = 90%)
- const requiredTotal = 90; // 90% for A grade (9 points)
- const currentPartial = caTotal + midSem;
- const requiredEndSem = Math.max(0, requiredTotal - currentPartial);
-
- // Calculate overall CGPA
- const overallCGPA = calculateOverallCGPA();
-
- // Display results
+ const marks = {
+ ct1: parseFloat(document.getElementById('ct1').value) || 0,
+ ct2: parseFloat(document.getElementById('ct2').value) || 0,
+ assignment: parseFloat(document.getElementById('assignment').value) || 0,
+ midSem: parseFloat(document.getElementById('midSem').value) || 0,
+ endSem: parseFloat(document.getElementById('endSem').value) || 0
+ };
+
+ subjectMarksByName[subject] = marks;
+
+ const { grade, points } = computeGradeFromMarks(marks);
+ const requiredEndSem = computeRequiredEndSemForGrade(marks, 'AA');
+ const overallCgpa = computeOverallCgpa();
+ const totalScore = computeTotalScoreFromMarks(marks);
+
const currentScore = document.getElementById('currentScore');
const currentGrade = document.getElementById('currentGrade');
const requirement = document.getElementById('requirement');
- const overallCGPAElement = document.getElementById('overallCGPA');
-
+ const overallCgpaElement = document.getElementById('overallCGPA');
+
if (currentScore) currentScore.textContent = `${Math.round(totalScore)}/100`;
- if (currentGrade) currentGrade.textContent = `${grade} (${gradePoints} points)`;
+ if (currentGrade) currentGrade.textContent = `${grade} (${points} points)`;
if (requirement) {
- requirement.textContent = requiredEndSem <= 60 ? `${Math.round(requiredEndSem)}/60 in End Sem` : 'Target not achievable';
+ requirement.textContent = requiredEndSem <= 60
+ ? `${Math.round(requiredEndSem)}/60 in End Sem`
+ : 'Target not achievable';
}
- if (overallCGPAElement) overallCGPAElement.textContent = overallCGPA.toFixed(2);
-
+ if (overallCgpaElement) overallCgpaElement.textContent = overallCgpa.toFixed(2);
+
const results = document.getElementById('cgpaResults');
if (results) {
results.classList.remove('hidden');
@@ -705,68 +702,84 @@ function calculateCGPA() {
}
}
-function calculateOverallCGPA() {
+function computeTotalScoreFromMarks(marks) {
+ const caScores = [marks.ct1, marks.ct2, marks.assignment].sort((a, b) => b - a);
+ const caTotal = caScores[0] + caScores[1];
+ return caTotal + marks.midSem + marks.endSem;
+}
+
+function computeGradeFromMarks(marks) {
+ const totalScore = computeTotalScoreFromMarks(marks);
+ const percentage = (totalScore / 100) * 100;
+
+ let grade = 'F';
+ let points = 0;
+
+ for (const [gradeName, gradeData] of Object.entries(GRADE_SCALE)) {
+ if (percentage >= gradeData.min) {
+ grade = gradeName;
+ points = gradeData.points;
+ break;
+ }
+ }
+
+ return { grade, points };
+}
+
+function computeRequiredEndSemForGrade(marks, gradeName) {
+ const targetTotal = GRADE_SCALE[gradeName].min;
+ const caScores = [marks.ct1, marks.ct2, marks.assignment].sort((a, b) => b - a);
+ const caTotal = caScores[0] + caScores[1];
+ const currentPartial = caTotal + marks.midSem;
+
+ return Math.max(0, targetTotal - currentPartial);
+}
+
+function computeOverallCgpa() {
let totalCredits = 0;
let weightedPoints = 0;
-
- Object.entries(subjects).forEach(([subjectName, subjectData]) => {
- const marks = subjectMarks[subjectName];
+
+ Object.entries(SUBJECT_CATALOG).forEach(([subjectName, subjectData]) => {
+ const marks = subjectMarksByName[subjectName];
if (marks) {
- const caScores = [marks.ct1, marks.ct2, marks.assignment].sort((a, b) => b - a);
- const caTotal = caScores[0] + caScores[1];
- const totalScore = caTotal + marks.midSem + marks.endSem;
- const percentage = (totalScore / 100) * 100;
-
- let gradePoints = 0;
- for (const [, gradeData] of Object.entries(gradeScale)) {
- if (percentage >= gradeData.min) {
- gradePoints = gradeData.points;
- break;
- }
- }
-
+ const { points } = computeGradeFromMarks(marks);
totalCredits += subjectData.credits;
- weightedPoints += gradePoints * subjectData.credits;
+ weightedPoints += points * subjectData.credits;
}
});
-
+
return totalCredits > 0 ? weightedPoints / totalCredits : 0;
}
-// SECTION 4: Performance Modes
-function calculateModeDurations() {
- const totalChapters = Object.values(subjects).reduce((sum, subject) => sum + subject.totalChapters, 0);
-
+function renderModeDurations() {
+ const totalChapters = Object.values(SUBJECT_CATALOG).reduce((sum, subject) => sum + subject.totalChapters, 0);
+
const easyDuration = document.getElementById('easyDuration');
const normalDuration = document.getElementById('normalDuration');
const intenseChapters = document.getElementById('intenseChapters');
-
+
if (easyDuration) easyDuration.textContent = `${totalChapters} days`;
if (normalDuration) normalDuration.textContent = `${Math.ceil(totalChapters / 2)} days`;
if (intenseChapters) intenseChapters.textContent = `${Math.ceil(totalChapters / 7)}`;
}
-function selectPerformanceMode(mode) {
- console.log('Selecting performance mode:', mode);
-
- // Update UI
+function handleModeSelection(mode) {
document.querySelectorAll('.mode-card').forEach(card => {
card.classList.remove('selected');
});
+
const selectedCard = document.querySelector(`[data-mode="${mode}"]`);
if (selectedCard) {
selectedCard.classList.add('selected');
}
-
- // Update study mode selector in Section 1
+
const studyModeSelect = document.getElementById('studyMode');
if (studyModeSelect) {
studyModeSelect.value = mode;
}
-
- // Show mode details
- showModeBreakdown(mode);
-
+
+ renderModeBreakdown(mode);
+
const details = document.getElementById('selectedModeDetails');
if (details) {
details.classList.remove('hidden');
@@ -774,18 +787,18 @@ function selectPerformanceMode(mode) {
}
}
-function showModeBreakdown(mode) {
+function renderModeBreakdown(mode) {
const breakdown = document.getElementById('modeBreakdown');
if (!breakdown) return;
-
- const subjectList = Object.entries(subjects).sort((a, b) => b[1].credits - a[1].credits);
-
+
+ const subjectList = Object.entries(SUBJECT_CATALOG).sort((a, b) => b[1].credits - a[1].credits);
+
breakdown.innerHTML = '';
-
+
subjectList.forEach(([subjectName, subjectData]) => {
const item = document.createElement('div');
item.className = 'breakdown-item';
-
+
let allocation = '';
if (mode === 'easy') {
allocation = `${subjectData.totalChapters} days (1 chapter/day)`;
@@ -795,18 +808,33 @@ function showModeBreakdown(mode) {
const priority = subjectData.credits >= 3 ? 'High Priority' : 'Medium Priority';
allocation = `${priority} - ${subjectData.totalChapters} chapters`;
}
-
+
item.innerHTML = `
-
${subjectName} (${subjectData.credits} credits)
+
${escapeHtml(subjectName)} (${subjectData.credits} credits)
${allocation}
`;
-
+
breakdown.appendChild(item);
});
}
-// Make functions globally available for onclick handlers
-window.toggleFolder = toggleFolder;
-window.toggleChapter = toggleChapter;
-window.removeMaterial = removeMaterial;
-window.updateProgress = updateProgress;
+function escapeHtml(text) {
+ return String(text)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function sanitizeHtmlId(value) {
+ return String(value)
+ .replace(/[^a-zA-Z0-9-]+/g, '-')
+ .replace(/^-+|-+$/g, '')
+ .toLowerCase();
+}
+
+window.toggleFolder = toggleSubjectFolder;
+window.toggleChapter = toggleChapterFolder;
+window.removeMaterial = deleteMaterial;
+window.updateProgress = toggleDayCompletion;
\ No newline at end of file