planModel.js Core logic
Plan display helpers and PDF export. Provides functions for computing semester credit totals, retrieving ordered course lists, and finding edge connections. Also contains the async PDF export that builds a print-ready HTML document.
Exported functions
shOverrides for variable-credit courses. Adds bonusSH[semId] for AP/transfer credit. Used in semester row headers.semOrders[semId] (filtered to only those still in this semester), then appends any remaining courses not in the order array. Handles the case where a user places a new course without any explicit order for it.from or to end. Used by RelationLines when a course is selected — it needs all related courses to draw lines to/from them. Each edge has { from, to, type }.Document contents:
- Page 1: NUPath coverage grid (12 attributes, WF excluded), major requirements tree, minor 1 and 2 trees, substitution list, total SH done/planned/required
- Page 2+: per-semester blocks — co-op/internship rows (company logo + role) or course rows (subject pill, NUPath badges, SH). Past semesters marked "completed", current "in progress".
PDF export mechanism
page 2+: semester schedule blocks
page-break CSS between pages PM->>B: new Blob([html], {type:"text/html"}) PM->>B: URL.createObjectURL(blob) → blobUrl PM->>B: window.open(blobUrl, "_blank") → w alt pop-up blocked (w === null) PM->>B: alert("Allow pop-ups…") PM->>B: URL.revokeObjectURL(blobUrl) else pop-up allowed PM->>B: setTimeout(() => w.print(), 400ms) note over B: 400ms lets new tab finish rendering B->>B: print dialog opens B-->>PM: w.onafterprint fires PM->>B: w.close() PM->>B: URL.revokeObjectURL(blobUrl) end
Company logo (CompanyLogo.jsx)
Co-op and internship blocks display the company's logo. The logo is fetched from Google's S2 favicon service — no API key required, always cross-origin:
https://www.google.com/s2/favicons?domain=microsoft.com&sz=128
The CompanyLogo component starts with visibility: hidden (not display: none) so the onLoad event still fires. It becomes visible on successful load and stays hidden on error — so a missing or unknown domain simply shows nothing, no broken image icon. The parent must supply key={domain} so React remounts the component (and re-fetches) when the domain changes.
The same URL is used in the PDF export (via pdfFaviconUrl() in planModel.js) with an inline onerror="this.style.display='none'" fallback.