summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html44
-rw-r--r--script.js237
-rw-r--r--style.css37
3 files changed, 217 insertions, 101 deletions
diff --git a/index.html b/index.html
index 757de9d..806ab96 100644
--- a/index.html
+++ b/index.html
@@ -8,28 +8,38 @@
</head>
<body>
<div class="container">
+ <noscript>
+ <div>
+ <p>Sorry, this doesn't do anything without JS.</p>
+ </div>
+ </noscript>
+ <div>
+ Sample queries:
+ <span id="sample-queries">
+ <a href="#" class="sample-query">biol</a>,
+ <a href="#" class="sample-query">SB/ACTG 2010 3.00</a>,
+ <a href="#" class="sample-query">ADMS 1000 3</a>,
+ <a href="#" class="sample-query">LE EECS 3000 Cr=3.00 A Term:F Professional Practice in Computing</a>
+ </span>
<div>
<form method="GET">
- <input type="text" id="input" size="60">
- <select id="terms" size=3></select>
+ <table>
+ <tr>
+ <td><input type="text" id="input"></td>
+ <td><select id="sessions" size="2"></select></td>
+ </tr>
+ </table>
</form>
</div>
+ <hr>
+ <div>
+ <span id="error"></span>
+ </div>
+ <div id="link-container">
+ <p><a id="link" rel="noopener noreferrer">Course Query</a></p>
+ </div>
<div>
- <table>
- <thead>
- <th>Faculty</th>
- <th>Dept./Subj.</th>
- <th>Number</th>
- <th>Credits</th>
- </thead>
- <tbody>
- <td id="faculty"></td>
- <td id="dept"></td>
- <td id="crsn"></td>
- <td id="cred"></td>
- </tbody>
- </table>
- <a id="link">Course Query</a>
+ <span id="warning"></span>
</div>
</div>
</body>
diff --git a/script.js b/script.js
index 6325077..83ed58e 100644
--- a/script.js
+++ b/script.js
@@ -1,7 +1,9 @@
-var FACS = new Set();
-var DEPTS = new Map();
+(function () {
+"use strict";
+const FACS = new Set();
+const DEPTS = new Map();
// maintain: CDM Advanced Search
-// console.log(Array.prototype.map.call(document.getElementsByName("subjectPopup")[0].children, (e)=>'"' + e.textContent.match(/[A-Z]{2,4}/)[0] + '"').join(", "))
+// console.log(Array.prototype.map.call(document.getElementsByName("subjectPopup")[0].children,(e)=>'"'+e.textContent.match(/[A-Z]{2,4}/)[0] + '"').join(", "))
[
["AP", "ADMB", "ADMS", "ANTH", "ARB", "ASL", "CCY", "CDNS", "CH", "CLST", "CLTR", "COGS", "COMN", "CRIM", "DEMS", "DLLL", "ECON", "EN", "ESL", "FND", "FR", "GCIN", "GEOG", "GER", "GK", "GKM", "GWST", "HEB", "HIST", "HREQ", "HRM", "HUMA", "INDG", "INDV", "IT", "ITEC", "JC", "JP", "KOR", "LA", "LASO", "LING", "LLS", "MIST", "MODR", "PERS", "PHIL", "POLS", "POR", "PPAS", "PRWR", "RU", "SOCI", "SOSC", "SOWK", "SP", "SWAH", "SXST", "TESL", "TYP", "WKLS", "WRIT"],
["ED", "AUCO", "BBED", "BIOL", "CHEM", "DANC", "DEST", "DRAA", "ECON", "EDFE", "EDFR", "EDIN", "EDIS", "EDJI", "EDPJ", "EDPR", "EDST", "EDUC", "EN", "ENVS", "FAST", "FREN", "GEOG", "HEB", "HIST", "INDS", "LAW", "LLDV", "MATH", "MUSI", "ORCO", "PHED", "PHIL", "PHYS", "POLS", "PRAC", "SCIE", "SLGS", "SOSC", "TECH", "TLSE", "VISA"],
@@ -14,97 +16,204 @@ var DEPTS = new Map();
["SB", "ACTG", "ARTM", "BSUS", "DCAD", "ECON", "EMBA", "ENTR", "EXCH", "FINE", "FNEN", "FNSV", "HIMP", "IBUS", "MBA", "INTL", "MACC", "MBAN", "MFIN", "MGMT", "MINE", "MKTG", "MSTM", "OMIS", "ORGS", "OVGS", "PLCY", "PROP", "PUBL", "SGMT", "SOCM"],
["SC", "BC", "BCHM", "BIOL", "BPHS", "CHEM", "COOP", "ENVB", "GEOG", "ISCI", "MATH", "NATS", "PHYS", "SENE", "STS"]
].forEach((arr) => {
- var faculty = arr.shift();
+ let faculty = arr.shift();
FACS.add(faculty);
arr.forEach((dept) => DEPTS.set(dept, faculty));
});
-var TOKEN_DELIM_RE = /[^A-Za-z0-9.]/;
-var CRSN_RE = /^[0-9]{4}$/;
-var DEPTCRSN_RE = /^([A-Z]{2,4})([0-9]{4})$/;
-var CRED_RE = /(?:Cr=)?([0-9]{1,2}\.[0-9]{1,2})/;
-var MAYBE_DEPT_RE = /[A-Z]{2,4}/;
-var USELESS_RE = /^(?:[A-Z])$/;
+const TOKEN_DELIM_RE = /[^A-Za-z0-9.=]/;
+const CRSN_RE = /^[0-9]{4}$/;
+const DEPTCRSN_RE = /^([A-Z]{2,4})([0-9]{4})$/;
+const CRED_RE = /^(?:Cr=([0-9]{1,2}(?:\.[0-9]{0,2})?)|([0-9]{1,2}\.[0-9]{0,2}))$/;
+const MAYBE_CRED_RE = /^[0-9]{1,2}$/;
+const MAYBE_DEPT_RE = /^[A-Z]{2,4}$/;
+const IGNORE_RE = /^[A-Z]$/;
-function warn(str) {
- console.warn(str);
-}
+let ycq_storage = JSON.parse(localStorage.ycq || "{}");
+if (!ycq_storage)
+ ycq_storage = {};
+ycq_storage.courses = ycq_storage.courses || {};
-function got(state, type, token, guess) {
- if (state[type])
- warn("already have " + type);
- state[type] = token;
+let now = new Date();
+let month = now.getMonth();
+// change to next summer on new year
+// begin next FW on July 1
+let fw_year, s_year;
+if (month >= 6) {
+ fw_year = now.getFullYear();
+} else {
+ fw_year = now.getFullYear() - 1;
}
-
-function fullyear(str) {
- return str.length == 2
- ? "" + CENTURY + str
- : str;
+s_year = now.getFullYear();
+let fw_sess = document.createElement("option");
+let fw_sess_val = "FW " + fw_year;
+fw_sess.value = fw_sess_val;
+fw_sess.textContent = `Fall/Winter ${fw_year}-${fw_year+1}`;
+let s_sess = document.createElement("option");
+let s_sess_val = "SU " + (s_year - 1);
+s_sess.value = s_sess_val;
+s_sess.textContent = `Summer ${s_year}`;
+switch (ycq_storage.sess) {
+ case fw_sess_val: fw_sess.selected = true; break;
+ case s_sess_val: s_sess.selected = true; break;
+ default:
+ /* assume enrolling for summer courses between March and May inclusive */
+ if (month >= 2 && month <= 4)
+ s_sess.selected = true;
+ else
+ fw_sess.selected = true;
}
+let ENGLISH_TYPES = {"fac": "faculty", "dept": "department/subject", "crsn": "course number", "cred": "credit number", "sess": "session (pick on the right)"};
+
document.addEventListener("DOMContentLoaded", function () {
- var faculty = document.querySelector("#faculty");
- var dept = document.querySelector("#dept");
- var crsn = document.querySelector("#crsn");
- var input = document.querySelector("#input");
- var link = document.querySelector("#link");
+ let sessions = document.querySelector("#sessions");
+ sessions.appendChild(fw_sess);
+ sessions.appendChild(s_sess);
- var defaults = {"fac": "AP", "dept": "ADMS", "crsn": "1000"};
+ let input = document.getElementById("input");
+ let link = document.getElementById("link");
+ let linkContainer = document.getElementById("link-container");
- var apply = function () {
- var state = {};
+ var crsn, cred;
- input.value.split(TOKEN_DELIM_RE).forEach(function (token) {
+ var state;
+
+ let apply = function () {
+ state = {dept: "", fac: "", crsn: "", cred: ""};
+ let multiple = new Set();
+ let warn = [], error = [];
+
+ let got = function (type, token) {
+ if (state[type])
+ multiple.add(ENGLISH_TYPES[type]);
+ state[type] = token;
+ };
+
+ let tryParse = function (token) {
+ let deptcrsn, cred;
if (DEPTS.has(token)) {
- got(state, "dept", token);
- return;
+ got("dept", token);
+ } else if (FACS.has(token)) {
+ got("fac", token);
+ } else if ((deptcrsn = DEPTCRSN_RE.exec(token))) {
+ got("dept", deptcrsn[1]);
+ got("crsn", deptcrsn[2]);
+ } else if ((cred = CRED_RE.exec(token))) {
+ got("cred", parseFloat(cred[1] || cred[2]).toFixed(2));
+ } else if (CRSN_RE.test(token)) {
+ got("crsn", token);
+ } else if (MAYBE_CRED_RE.test(token)) {
+ got("cred", parseFloat(token).toFixed(2));
+ warn.push(`assuming ${token} is the number of credits`);
+ } else if (MAYBE_DEPT_RE.test(token)) {
+ state.maybeDept = token;
+ } else if (!IGNORE_RE.test(token)) {
+ return false;
}
- if (FACS.has(token)) {
- got(state, "fac", token, true);
- return;
- }
- var deptcrsn = DEPTCRSN_RE.exec(token);
- if (deptcrsn) {
- got(state, "dept", deptcrsn[1]);
- got(state, "crsn", deptcrsn[2]);
- return;
- }
- var cred = CRED_RE.exec(token);
- if (cred) {
- got(state, "cred", parseFloat(cred[1]).toFixed(2));
- return;
- }
- if (CRSN_RE.test(token)) {
- got(state, "crsn", token);
- return;
- }
- if (MAYBE_DEPT_RE.test(token)) {
- got(state, "maybeDept", token);
- return;
- }
- if (USELESS_RE.test(token)) {
+ return true;
+ };
+
+ input.value.split(TOKEN_DELIM_RE).forEach(function (token) {
+ if (!token || tryParse(token) || tryParse(token.toUpperCase()))
return;
- }
- warn("don't understand search term: " + token);
+ warn.push("don't understand search term: " + token);
});
if (!state.dept && state.maybeDept) {
state.dept = state.maybeDept;
- warn("guessing department is " + state.maybeDept);
+ warn.push("guessing department is " + state.maybeDept);
}
if (!state.fac && state.dept) {
state.fac = DEPTS.get(state.dept);
if (!state.fac)
- warn("unknown department");
+ error.push("no faculty provided and unknown department");
+ }
+
+ if (state.crsn && !state.cred) {
+ if (ycq_storage.courses[state.dept + state.crsn]) {
+ state.cred = ycq_storage.courses[state.crsn];
+ warn.push(`using saved credit number for ${state.dept} ${state.crsn}: ${state.cred}`);
+ } else {
+ state.cred = "3.00";
+ warn.push("assuming the number of credits is 3.00");
+ }
+ }
+
+ let r_sess = document.getElementById("sessions").value;
+ let sess = r_sess.split(" ");
+ state.sess = sess[0];
+ state.year = parseInt(sess[1]);
+
+ if (state.fac && state.dept && state.crsn && state.cred && state.year && state.sess) {
+ link.href = `https://w2prod.sis.yorku.ca/Apps/WebObjects/cdm.woa/wa/crsq?fa=${state.fac}&sj=${state.dept}&cn=${state.crsn}&cr=${state.cred}&ay=${state.year}&ss=${state.sess}`;
+ link.innerHTML = `York University: ${state.fac}/${state.dept} ${state.crsn} ${state.cred} (${sessions.selectedOptions[0].innerHTML})`;
+ linkContainer.style = "display: block";
+ } else if (state.fac && state.dept && state.sess) {
+ link.href = `https://w2prod.sis.yorku.ca/Apps/WebObjects/cdm.woa/wa/crsq1?faculty=${state.fac}&subject=${state.dept}&academicyear=${state.year}&studysession=${state.sess}`;
+ link.innerHTML = `York University: ${state.fac}/${state.dept} Courses (${sessions.selectedOptions[0]. innerHTML})`;
+ linkContainer.style = "display: block";
+ } else {
+ linkContainer.style = "display: none";
+ error.push("insufficient search parameters. need at least department/subject. see examples above");
}
- console.log(state);
+ if (multiple.size)
+ warn.push("multiple terms for " + Array.from(multiple.values()).join(", ") + ", using last occurrence for each");
- if (state.fac && state.dept && state.crsn && state.cred && state.year && state.term)
- link.href = `https://w2prod.sis.yorku.ca/Apps/WebObjects/cdm.woa/wa/crsq?fa=${state.fac}&sj=${state.dept}&cn=${state.crsn}&cr=${state.cred}&ay=${state.year}&ss=${state.sess}`
+ document.getElementById("warning").innerHTML = warn.map(w => 'warning: ' + w).join("<br>");
+ document.getElementById("error").innerHTML = error.map(e => 'error: ' + e).join("<br>");
+
+ ycq_storage.sess = r_sess;
+ localStorage.ycq = JSON.stringify(ycq_storage);
};
input.addEventListener("input", apply);
+ sessions.addEventListener("change", apply);
+ document.getElementById("sample-queries").addEventListener("click", function (e) {
+ if (e.target.tagName == "A") {
+ e.preventDefault();
+ input.value = e.target.textContent;
+ apply();
+ input.focus();
+ }
+ });
+ var apply_crsn_cred = function () {
+ if (state.crsn && state.cred) {
+ ycq_storage.courses[state.dept + state.crsn] = state.cred;
+ localStorage.ycq = JSON.stringify(ycq_storage);
+ }
+ };
+ link.addEventListener("click", apply_crsn_cred);
+ document.forms[0].addEventListener("submit", function (e) {
+ e.preventDefault();
+ if (linkContainer.style.display == "block")
+ link.click();
+ });
+ input.addEventListener("keydown", function (e) {
+ if (e.key == "Enter") {
+ e.preventDefault();
+ if (linkContainer.style.display == "block") {
+ // firefox silently ignores simulated link events with modifiers
+ if (/(?:Firefox|Gecko)\//.test(navigator.userAgent) && (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) {
+ if (e.ctrlKey) {
+ link.target = "_blank";
+ link.click();
+ link.target = "";
+ } else if (e.shiftKey) {
+ apply_crsn_cred();
+ window.open(link.href, "_blank",
+ "noopener,width=625,resizable,menubar,toolbar,location,personalbar,status,scrollbars");
+ } else {
+ link.click();
+ }
+ } else {
+ link.dispatchEvent(new MouseEvent("click", e));
+ }
+ }
+ }
+ });
apply();
});
+}());
diff --git a/style.css b/style.css
index 4ac2f88..1f40764 100644
--- a/style.css
+++ b/style.css
@@ -7,38 +7,35 @@ html {
box-sizing: inherit;
}
-body, h1, h2, h3, h4, h5, h6, p, ol, ul {
- margin: 0;
- padding: 0;
- font-weight: normal;
+.container {
+ margin: 5px 20%;
}
-ol, ul {
- list-style: none;
+div {
+ margin: 5px;
}
-img {
- max-width: 100%;
- height: auto;
+td, th {
+ padding: 5px;
}
-.container {
- margin: 5px;
+#input {
+ width: 100%;
+ font-size: 1.3em;
}
-div {
- margin: 5px;
+table {
+ width: 100%;
}
-table {
- border-collapse: collapse;
+.default {
+ color: #aaa;
}
-table, td, th {
- border: 1px solid black;
- padding: 5px;
+#error {
+ color: #d7191c;
}
-.default {
- color: #aaa;
+#warning {
+ color: #a6611a;
}