(function () {
'use strict';
const MASTER_KEY = 'enabled';
async function isEnabled() {
const { enabled } = await browser.storage.local.get(MASTER_KEY);
return enabled !== false;
}
function autoAcceptPrivacy() {
const modal = document.getElementById('myModal');
if (!modal) return;
const checkbox = document.getElementById('Confirmation');
const submit = document.getElementById('Submit');
if (checkbox && !checkbox.checked) checkbox.click();
if (submit) submit.click();
}
function watchPrivacyModal() {
autoAcceptPrivacy();
const observer = new MutationObserver(() => autoAcceptPrivacy());
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(() => observer.disconnect(), 30000);
}
async function autoLogin() {
const { profiles } = await browser.storage.local.get('profiles');
const active = (profiles || []).find((p) => p.enabled);
if (!active) return;
const userField = document.querySelector(
'input[name="Username"], input[id="Username"], input[name="UserName"], input[name="username"], input[id="UserName"], input[id="username"]'
);
const passField = document.querySelector('input[type="password"]');
if (!userField || !passField) return;
userField.value = active.username;
passField.value = active.password;
userField.dispatchEvent(new Event('input', { bubbles: true }));
passField.dispatchEvent(new Event('input', { bubbles: true }));
const form = userField.closest('form') || passField.closest('form');
if (!form) return;
setTimeout(() => {
const submitBtn = form.querySelector('input[type="submit"], button[type="submit"]');
if (submitBtn) submitBtn.click();
else form.submit();
}, 300);
}
async function fetchCourseDescription(courseCode, academicPeriod, academicYear, token) {
const body = new URLSearchParams();
body.set('Courses', courseCode);
body.set('AcademicPeriod', academicPeriod);
body.set('AcademicYear', academicYear);
body.set('AjaxFormVM.Controller', 'CourseSchedule');
body.set('AjaxFormVM.Action', 'CourseScheduleOffered');
body.set('AjaxFormVM.ResultTargetId', 'CourseScheduleOfferedList');
body.set('AjaxFormVM.LinkToReturn', '');
body.set('AjaxFormVM.LinkToReturnTargetId', '');
body.set('AjaxFormVM.FormHeader', 'Choose term to view courses offered');
body.set('AjaxFormVM.FormHeaderIcon', '');
body.set('AjaxFormVM.FormId', 'CourseScheduleCourseScheduleOffered');
body.set('AjaxFormVM.ShowCloseButton', 'True');
body.set('AjaxFormVM.PreventAutoUpdateTarget', 'True');
body.set('AjaxFormVM.CloseModalAfterSubmit', 'False');
body.set('AjaxFormVM.AjaxFormType', 'Search');
body.set('AjaxFormVM.FormNoteLocation', '');
body.set('AjaxFormVM.SubmitButton.FaIcon', '');
body.set('AjaxFormVM.SubmitButton.ButtonText', '');
body.set('AjaxFormVM.SubmitButton.ButtonSize', '');
body.set('__RequestVerificationToken', token);
const resp = await fetch('/CourseSchedule/CourseScheduleOffered?Length=14', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest'
},
body: body.toString(),
credentials: 'same-origin'
});
if (!resp.ok) throw new Error('Offered courses request failed: ' + resp.status);
const html = await resp.text();
const courses = ISMIStools.parseOfferedCourses(html);
const match = courses.find((c) => c.courseCode === courseCode);
return match ? match.description : '';
}
function extractTotalUnits(text) {
const m = /Total Units:\s*([\d.]+)/i.exec(text);
return m ? m[1] : '';
}
async function scrapeStudyLoad() {
const table = document.querySelector('table.table-forum');
if (!table) throw new Error('No study load table found on this page.');
const courses = ISMIStools.parseStudyLoadTable(table.outerHTML);
if (!courses.length) throw new Error('No courses found in study load table.');
const periodEl = document.getElementById('AcademicPeriod');
const yearEl = document.getElementById('AcademicYear');
const tokenEl = document.querySelector('input[name="__RequestVerificationToken"]');
const academicPeriod = periodEl ? periodEl.value : '';
const academicYear = yearEl ? yearEl.value : '';
const token = tokenEl ? tokenEl.value : '';
// Semester/year come from the search dropdowns the user picked.
// AcademicPeriod's selected option label (e.g. "1ST Semester") + year.
const periodLabel = (periodEl && periodEl.selectedOptions && periodEl.selectedOptions[0])
? periodEl.selectedOptions[0].textContent.trim()
: '';
const selectedPeriod = periodLabel || academicPeriod;
const selectedYear = academicYear;
const uniqueCodes = [...new Set(courses.map((c) => c.courseCode))];
const descMap = {};
for (const code of uniqueCodes) {
try {
descMap[code] = await fetchCourseDescription(code, academicPeriod, academicYear, token);
} catch (e) {
descMap[code] = '';
}
}
courses.forEach((c) => { c.description = descMap[c.courseCode] || ''; });
let header = {};
try {
const resp = await fetch('/Manage/Student', { credentials: 'same-origin' });
if (!resp.ok) throw new Error('Profile request failed: ' + resp.status);
const html = await resp.text();
header = ISMIStools.parseProfilePage(html);
} catch (e) {
header = {};
}
// Semester goes into the header so the PDF shows the dropdown's term/year.
header.semester = (selectedPeriod && selectedYear)
? selectedPeriod + ' ' + selectedYear
: (header.semester || '');
return { header, courses, period: selectedPeriod, year: selectedYear };
}
// ---- On-page Download PDF button ----
const BTN_ID = 'ismisHelperDownloadBtn';
function showToast(msg, isError) {
if (window.toastr) {
if (isError) window.toastr.error(msg);
else window.toastr.success(msg);
} else if (isError) {
alert(msg);
}
}
function findTotalUnitsP() {
const ps = document.querySelectorAll('#StudyLoad p');
for (const p of ps) {
if (/Total Units/i.test(p.textContent || '')) return p;
}
return null;
}
async function onDownloadPdf() {
const btn = document.getElementById(BTN_ID);
try {
btn.disabled = true;
btn.innerHTML = ' Downloading...';
const { header, courses } = await scrapeStudyLoad();
const included = ISMIStools.filterStudyLoadCourses(courses);
if (!included.length) throw new Error('No enrolled courses found.');
const totalUnits = ISMIStools.sumUnits(included);
const { truncateDescription } = await browser.storage.local.get('truncateDescription');
const res = await browser.runtime.sendMessage({
type: 'generate-pdf',
data: { header, courses: included, totalUnits, truncateDescription: truncateDescription !== false }
});
if (!res || !res.ok) throw new Error((res && res.error) || 'PDF generation failed');
showToast('PDF downloaded.');
} catch (err) {
showToast('Error: ' + ((err && err.message) || err), true);
} finally {
btn.disabled = false;
btn.innerHTML = ' Download PDF';
}
}
function injectDownloadButton() {
if (document.getElementById(BTN_ID)) return;
const unitsP = findTotalUnitsP();
if (!unitsP) return;
const wrapper = document.createElement('div');
wrapper.style.display = 'flex';
wrapper.style.alignItems = 'center';
wrapper.style.justifyContent = 'space-between';
wrapper.style.margin = '10px 0';
const btn = document.createElement('button');
btn.id = BTN_ID;
btn.type = 'button';
btn.className = 'btn green';
btn.innerHTML = ' Download PDF';
btn.addEventListener('click', onDownloadPdf);
unitsP.parentNode.insertBefore(wrapper, unitsP);
wrapper.appendChild(btn);
wrapper.appendChild(unitsP);
}
function tryInjectButton() {
if (document.getElementById(BTN_ID)) return;
if (document.querySelector('#StudyLoad table.table-forum')) injectDownloadButton();
}
function watchStudyLoad() {
tryInjectButton();
const observer = new MutationObserver((mutations) => {
const inStudyLoad = mutations.some(
(m) => m.target && m.target.closest && m.target.closest('#StudyLoad')
);
if (inStudyLoad) tryInjectButton();
});
observer.observe(document.body, { childList: true, subtree: true });
}
(async function init() {
if (!(await isEnabled())) return;
watchPrivacyModal();
if (/\/Account\/Login/i.test(location.href)) autoLogin();
if (document.getElementById('StudyLoad')) watchStudyLoad();
})();
})();