diff options
-rw-r--r-- | index.html | 44 | ||||
-rw-r--r-- | script.js | 237 | ||||
-rw-r--r-- | style.css | 37 |
3 files changed, 217 insertions, 101 deletions
@@ -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> @@ -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(); }); +}()); @@ -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; } |