From 6d1016e2dfb3d0b10d8e7203dd217382821ab42a Mon Sep 17 00:00:00 2001 From: "Alex Xu (Hello71)" Date: Tue, 18 Sep 2018 16:06:59 -0400 Subject: add most features --- index.html | 44 +++++++----- script.js | 237 ++++++++++++++++++++++++++++++++++++++++++++----------------- style.css | 37 +++++----- 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 @@
+ +
+ Sample queries: + + biol, + SB/ACTG 2010 3.00, + ADMS 1000 3, + LE EECS 3000 Cr=3.00 A Term:F Professional Practice in Computing +
- - + + + + + +
+
+
+ +
+
- - - - - - - - - - - - - -
FacultyDept./Subj.NumberCredits
- Course Query +
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("
"); + document.getElementById("error").innerHTML = error.map(e => 'error: ' + e).join("
"); + + 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; } -- cgit v1.2.3-70-g09d2