213 lines
7.7 KiB
JavaScript
213 lines
7.7 KiB
JavaScript
'use strict';
|
|
|
|
const $ = (sel) => document.querySelector(sel);
|
|
|
|
const MASTER_KEY = 'enabled';
|
|
const PROFILES_KEY = 'profiles';
|
|
|
|
let editingProfileId = null;
|
|
|
|
// ---- Master toggle ----
|
|
async function loadMaster() {
|
|
const { enabled } = await browser.storage.local.get(MASTER_KEY);
|
|
$('#masterToggle').checked = enabled !== false;
|
|
$('#masterLabel').textContent = enabled === false ? 'Disabled' : 'Enabled';
|
|
}
|
|
$('#masterToggle').addEventListener('change', async (e) => {
|
|
await browser.storage.local.set({ [MASTER_KEY]: e.target.checked });
|
|
$('#masterLabel').textContent = e.target.checked ? 'Enabled' : 'Disabled';
|
|
});
|
|
|
|
// ---- Profiles ----
|
|
async function getProfiles() {
|
|
const { profiles } = await browser.storage.local.get(PROFILES_KEY);
|
|
return profiles || [];
|
|
}
|
|
async function saveProfiles(profiles) {
|
|
await browser.storage.local.set({ [PROFILES_KEY]: profiles });
|
|
}
|
|
|
|
function renderProfiles(profiles) {
|
|
const list = $('#profileList');
|
|
list.innerHTML = '';
|
|
for (const p of profiles) {
|
|
const li = document.createElement('li');
|
|
const name = document.createElement('div');
|
|
name.className = 'name';
|
|
name.innerHTML = '<strong>' + escapeHtml(p.name) + '</strong><br><span class="user">' + escapeHtml(p.username) + '</span>';
|
|
const toggle = document.createElement('input');
|
|
toggle.type = 'checkbox';
|
|
toggle.checked = !!p.enabled;
|
|
toggle.title = 'Enable/disable';
|
|
toggle.addEventListener('change', async () => {
|
|
p.enabled = toggle.checked;
|
|
await saveProfiles(profiles);
|
|
});
|
|
const edit = document.createElement('button');
|
|
edit.className = 'btn';
|
|
edit.textContent = 'Edit';
|
|
edit.addEventListener('click', () => openProfileForm(p));
|
|
const del = document.createElement('button');
|
|
del.className = 'btn';
|
|
del.textContent = 'Delete';
|
|
del.addEventListener('click', async () => {
|
|
await saveProfiles(profiles.filter((x) => x.id !== p.id));
|
|
renderProfiles(await getProfiles());
|
|
});
|
|
li.append(toggle, name, edit, del);
|
|
list.appendChild(li);
|
|
}
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return String(s).replace(/[&<>"']/g, (c) => ({
|
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
|
}[c]));
|
|
}
|
|
|
|
function openProfileForm(p) {
|
|
editingProfileId = p ? p.id : null;
|
|
$('#pName').value = p ? p.name : '';
|
|
$('#pUsername').value = p ? p.username : '';
|
|
$('#pPassword').value = p ? p.password : '';
|
|
$('#pEnabled').checked = p ? !!p.enabled : true;
|
|
$('#profileForm').hidden = false;
|
|
}
|
|
$('#addProfile').addEventListener('click', () => openProfileForm(null));
|
|
$('#cancelProfile').addEventListener('click', () => { $('#profileForm').hidden = true; });
|
|
$('#saveProfile').addEventListener('click', async () => {
|
|
const profiles = await getProfiles();
|
|
const data = {
|
|
id: editingProfileId || Date.now().toString(),
|
|
name: $('#pName').value.trim(),
|
|
username: $('#pUsername').value.trim(),
|
|
password: $('#pPassword').value,
|
|
enabled: $('#pEnabled').checked
|
|
};
|
|
if (!data.name || !data.username) return;
|
|
if (editingProfileId) {
|
|
const i = profiles.findIndex((x) => x.id === editingProfileId);
|
|
if (i >= 0) profiles[i] = data;
|
|
} else {
|
|
profiles.push(data);
|
|
}
|
|
await saveProfiles(profiles);
|
|
$('#profileForm').hidden = true;
|
|
renderProfiles(profiles);
|
|
});
|
|
|
|
// ---- Tabs ----
|
|
document.querySelectorAll('.tabs button').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
document.querySelectorAll('.tabs button').forEach((b) => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
$('#tab-login').hidden = btn.dataset.tab !== 'login';
|
|
$('#tab-studyload').hidden = btn.dataset.tab !== 'studyload';
|
|
});
|
|
});
|
|
|
|
// ---- Study Load ----
|
|
function setStatus(msg, cls) {
|
|
const el = $('#status');
|
|
el.textContent = msg;
|
|
el.className = 'status ' + (cls || '');
|
|
}
|
|
|
|
function renderCourses(courses) {
|
|
const box = $('#coursesEditor');
|
|
box.innerHTML = '';
|
|
courses.forEach((c, i) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'course-row';
|
|
row.innerHTML =
|
|
'<div class="grid">' +
|
|
'<input data-i="' + i + '" data-f="group" placeholder="Group" value="' + escapeHtml(c.group || '') + '">' +
|
|
'<input data-i="' + i + '" data-f="courseCode" placeholder="Course code" value="' + escapeHtml(c.courseCode || '') + '">' +
|
|
'<input data-i="' + i + '" data-f="description" placeholder="Description" value="' + escapeHtml(c.description || '') + '">' +
|
|
'<input data-i="' + i + '" data-f="units" placeholder="Units" value="' + escapeHtml(c.units || '') + '">' +
|
|
'<input data-i="' + i + '" data-f="teacher" placeholder="Teacher" value="' + escapeHtml(c.teacher || '') + '">' +
|
|
'<input data-i="' + i + '" data-f="status" placeholder="Status" value="' + escapeHtml(c.status || '') + '">' +
|
|
'</div>' +
|
|
'<input data-i="' + i + '" data-f="schedule" placeholder="Schedule" value="' + escapeHtml(c.schedule || '') + '">' +
|
|
'<button class="btn remove-course" data-i="' + i + '">Remove</button>';
|
|
box.appendChild(row);
|
|
});
|
|
box.querySelectorAll('input').forEach((inp) => {
|
|
inp.addEventListener('input', () => {
|
|
const i = +inp.dataset.i;
|
|
const f = inp.dataset.f;
|
|
studyLoadCourses[i][f] = inp.value;
|
|
});
|
|
});
|
|
box.querySelectorAll('.remove-course').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
studyLoadCourses.splice(+btn.dataset.i, 1);
|
|
renderCourses(studyLoadCourses);
|
|
});
|
|
});
|
|
}
|
|
|
|
let studyLoadCourses = [];
|
|
|
|
$('#loadFromPortal').addEventListener('click', async () => {
|
|
setStatus('Loading from portal...');
|
|
try {
|
|
const res = await browser.runtime.sendMessage({ type: 'scrape-study-load' });
|
|
if (!res || !res.ok) throw new Error((res && res.error) || 'Scrape failed');
|
|
const d = res.data;
|
|
$('#hFullName').value = d.header.fullName || '';
|
|
$('#hStudentId').value = d.header.studentId || '';
|
|
$('#hProgram').value = d.header.program || '';
|
|
$('#hYearLevel').value = d.header.yearLevel || '';
|
|
$('#hSemester').value = d.header.semester || '';
|
|
$('#hDateEnrolled').value = d.header.dateEnrolled || '';
|
|
$('#hDateRange').value = d.header.dateRange || '';
|
|
$('#totalUnits').value = d.totalUnits || '';
|
|
studyLoadCourses = d.courses || [];
|
|
renderCourses(studyLoadCourses);
|
|
$('#studyLoadForm').hidden = false;
|
|
setStatus('Loaded. Review and generate.', 'ok');
|
|
} catch (err) {
|
|
setStatus('Could not load from portal: ' + err.message + ' — enter manually below.', 'error');
|
|
studyLoadCourses = [];
|
|
renderCourses(studyLoadCourses);
|
|
$('#studyLoadForm').hidden = false;
|
|
}
|
|
});
|
|
|
|
$('#addCourse').addEventListener('click', () => {
|
|
studyLoadCourses.push({ group: '', courseCode: '', description: '', units: '', teacher: '', schedule: '', status: '' });
|
|
renderCourses(studyLoadCourses);
|
|
});
|
|
|
|
$('#generatePdf').addEventListener('click', async () => {
|
|
const data = {
|
|
header: {
|
|
fullName: $('#hFullName').value.trim(),
|
|
studentId: $('#hStudentId').value.trim(),
|
|
program: $('#hProgram').value.trim(),
|
|
yearLevel: $('#hYearLevel').value.trim(),
|
|
semester: $('#hSemester').value.trim(),
|
|
dateEnrolled: $('#hDateEnrolled').value.trim(),
|
|
dateRange: $('#hDateRange').value.trim()
|
|
},
|
|
courses: studyLoadCourses,
|
|
totalUnits: $('#totalUnits').value.trim()
|
|
};
|
|
if (!data.courses.length) { setStatus('Add at least one course.', 'error'); return; }
|
|
setStatus('Generating PDF...');
|
|
try {
|
|
const res = await browser.runtime.sendMessage({ type: 'generate-pdf', data });
|
|
if (!res || !res.ok) throw new Error((res && res.error) || 'Generation failed');
|
|
setStatus('PDF downloaded.', 'ok');
|
|
} catch (err) {
|
|
setStatus('Error: ' + err.message, 'error');
|
|
}
|
|
});
|
|
|
|
// ---- Init ----
|
|
(async function init() {
|
|
await loadMaster();
|
|
renderProfiles(await getProfiles());
|
|
})();
|