(function () { "use strict"; // as of 2018, LW does not use CDM, but leave it in just in case const FACS = new Set(["AP", "ED", "ES", "FA", "GL", "GS", "HH", "LE", "LW", "SB", "SC"]); const SUBJS = { "ACTG": ["SB"], "ADMB": ["AP"], "ADMS": ["AP"], "ANTH": ["AP", "GS"], "ARB": ["AP"], "ARTH": ["FA", "GS"], "ARTM": ["SB"], "ASL": ["AP"], "AUCO": ["ED"], "BBED": ["ED"], "BC": ["SC"], "BCHM": ["SC"], "BIOL": ["SC", "GL", "GS", "ED"], "BLIS": ["GS"], "BPHS": ["SC"], "BSUS": ["SB"], "BUEC": ["GL"], "CAT": ["GL"], "CCY": ["AP"], "CDIS": ["GS"], "CDNS": ["AP", "GL"], "CGTA": ["GS"], "CH": ["AP"], "CHEM": ["SC", "GS", "ED"], "CIVL": ["LE", "GS"], "CLST": ["AP"], "CLTR": ["AP"], "CMCT": ["GS"], "COGS": ["AP"], "COMN": ["AP"], "COMS": ["GL"], "COOP": ["LE", "SC"], "CRIM": ["AP"], "CSLA": ["GL"], "DANC": ["FA", "GS", "ED"], "DATT": ["FA"], "DCAD": ["SB"], "DEMS": ["AP", "GS"], "DESN": ["FA"], "DEST": ["ED"], "DIGM": ["GS"], "DLLL": ["AP"], "DRAA": ["ED"], "DRST": ["GL"], "DVST": ["GS"], "ECON": ["AP", "GL", "SB", "GS", "ED"], "EDFE": ["ED"], "EDFR": ["ED"], "EDIN": ["ED"], "EDIS": ["ED"], "EDJI": ["ED"], "EDPJ": ["ED"], "EDPR": ["ED"], "EDST": ["ED"], "EDUC": ["ED", "GS"], "EECS": ["LE", "GS"], "EIL": ["GS"], "EMBA": ["SB"], "EN": ["AP", "GL", "GS", "ED"], "ENG": ["LE", "GS"], "ENSL": ["GL"], "ENTR": ["SB"], "ENVB": ["SC"], "ENVS": ["GS", "ED"], "ESL": ["AP"], "ESS": ["GS"], "ESSE": ["LE"], "EXCH": ["SB"], "FACC": ["GS"], "FACS": ["FA"], "FAST": ["ED"], "FILM": ["FA", "GS"], "FINE": ["SB"], "FND": ["AP"], "FNEN": ["SB"], "FNSV": ["SB"], "FR": ["AP"], "FRAN": ["GL"], "FREN": ["ED", "GS"], "FSL": ["GL"], "GCIN": ["AP"], "GEOG": ["AP", "SC", "GS", "ED"], "GER": ["AP"], "GFWS": ["GS"], "GK": ["AP"], "GKM": ["AP"], "GWST": ["AP", "GL"], "HEB": ["AP", "ED"], "HIMP": ["SB"], "HIST": ["AP", "GL", "GS", "ED"], "HLST": ["HH"], "HLTH": ["GS"], "HREQ": ["AP"], "HRM": ["AP", "GS"], "HUMA": ["AP", "GL", "GS"], "IBUS": ["SB"], "IHST": ["HH"], "ILST": ["GL"], "INDG": ["AP"], "INDS": ["ED"], "INDV": ["AP", "FA"], "INST": ["GS"], "INTE": ["GS"], "INTL": ["SB"], "ISCI": ["SC"], "IT": ["AP"], "ITEC": ["AP", "GL", "GS"], "JC": ["AP"], "JP": ["AP"], "KAHS": ["GS"], "KINE": ["HH"], "KOR": ["AP"], "LA": ["AP"], "LAL": ["GS"], "LASO": ["AP"], "LAW": ["ED", "GS", "LW"], "LIN": ["GL"], "LING": ["AP"], "LLDV": ["ED"], "LLS": ["AP"], "LREL": ["GS"], "LYON": ["GL"], "MACC": ["SB"], "MATH": ["SC", "GL", "ED", "GS"], "MBA": ["SB"], "MBAN": ["SB"], "MDES": ["GS"], "MECH": ["GS"], "MFIN": ["SB"], "MGMT": ["SB"], "MINE": ["SB"], "MIST": ["AP"], "MKTG": ["SB"], "MODR": ["AP", "GL"], "MSTM": ["SB"], "MUSI": ["FA", "GS", "ED"], "NATS": ["SC", "GL"], "NURS": ["HH", "GS"], "OMIS": ["SB"], "ORCO": ["ED"], "ORGS": ["SB"], "OVGS": ["SB", "GS"], "PACC": ["GS"], "PANF": ["FA"], "PERS": ["AP"], "PHED": ["ED"], "PHIL": ["AP", "GL", "GS", "ED"], "PHYS": ["SC", "GL", "GS", "ED"], "PIA": ["GS"], "PKIN": ["HH"], "PLCY": ["SB"], "POLS": ["AP", "GL", "GS", "ED"], "POR": ["AP"], "PPAL": ["GS"], "PPAS": ["AP"], "PRAC": ["ED"], "PROP": ["SB"], "PRWR": ["AP"], "PSYC": ["GS", "GL"], "PUBL": ["SB"], "RU": ["AP"], "SCIE": ["ED"], "SENE": ["SC"], "SGMT": ["SB"], "SLGS": ["ED"], "SLST": ["GS"], "SOCI": ["AP", "GL", "GS"], "SOSC": ["AP", "GL", "ED"], "SOWK": ["AP", "GS"], "SP": ["AP", "GL"], "SPTH": ["GS"], "STS": ["GS"], "SWAH": ["AP"], "SXST": ["AP", "GL"], "TECH": ["ED"], "TESL": ["AP"], "THEA": ["FA", "GS"], "THST": ["GS"], "TLSE": ["ED"], "TRAN": ["GL", "GS"], "TRAS": ["GS"], "TXLW": ["GS"], "TYP": ["AP"], "VISA": ["FA", "GS"], "WKLS": ["AP"] } 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}$/; function parse_ycq_s(str) { let ycq_s = JSON.parse(str || "{}"); ycq_s.course_creds = ycq_s.course_creds || {}; ycq_s.course_subjs = ycq_s.course_subjs || {}; ycq_s.course_facs = ycq_s.course_facs || {}; ycq_s.subj_facs = ycq_s.subj_facs || {}; ycq_s.default_facs = ycq_s.default_facs || []; return ycq_s; } var cached_ycq_s = parse_ycq_s(localStorage.ycq); 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; } 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 (cached_ycq_s.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", "subj": "subject", "crsn": "course number", "cred": "credit number" }; function ready() { // when embedded in the webextension, the protocol will be moz-extension:// // or chrome:// or something like that if (!/^(?:https?|file):$/.test(location.protocol)) { document.getElementById("ywh-ad").style.display = "none"; } let sessions = document.getElementById("sessions"); sessions.appendChild(fw_sess); sessions.appendChild(s_sess); let input = document.getElementById("input"); let link = document.getElementById("link"); let linkContainer = document.getElementById("link-container"); let state; let add_li = function (l, t) { var e = document.createElement("li"); e.innerHTML = t; l.appendChild(e); }; let apply = function () { state = {subj: "", fac: "", crsn: "", cred: ""}; let multiple = new Set(); let curWarn = document.getElementById("warning-list"); let warn = curWarn.cloneNode(false); let curError = document.getElementById("error-list"); let error = curError.cloneNode(false); let got = function (type, token) { if (state[type]) multiple.add(ENGLISH_TYPES[type]); state[type] = token; }; let tryParse = function (token) { let subjcrsn, cred; if (SUBJS.hasOwnProperty(token)) { got("subj", token); } else if (FACS.has(token)) { got("fac", token); } else if ((subjcrsn = DEPTCRSN_RE.exec(token))) { got("subj", subjcrsn[1]); got("crsn", subjcrsn[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)); add_li(warn, `assuming ${token} is the number of credits`); } else if (MAYBE_DEPT_RE.test(token)) { state["maybeSubj"] = token; } else { return false; } return true; }; let unk_tokens = []; input.value.split(TOKEN_DELIM_RE).forEach(function (token) { if (!token || tryParse(token) || tryParse(token.toUpperCase())) return; unk_tokens.push(token); }); if (!state["subj"] && state["maybeSubj"]) { state["subj"] = state["maybeSubj"]; add_li(warn, `guessing subject is "${state["maybeSubj"]}".`); } if (!state["subj"] && state["crsn"] && cached_ycq_s.course_subjs[state["crsn"]]) { state["subj"] = cached_ycq_s.course_subjs[state["crsn"]]; add_li(warn, `assuming ${state["crsn"]} means ${state["subj"]} ${state["crsn"]}, because you last used ${state["subj"]} for ${state["crsn"]}.`); } if (!state["subj"] && cached_ycq_s.default_subj) { state["subj"] = cached_ycq_s.default_subj; add_li(warn, `using ${state["subj"]}, because that was your most recently queried subject.`); } if (!state["fac"] && state["subj"]) { // if no faculty, try: // 1. most recent faculty for this course // 2. most recent faculty for this subject // 3. most recent compatible faculty // 4. first compatible faculty let compat_facs = SUBJS[state["subj"]]; // needs to be a function so it can remove the current selection let other_options = function () { if (compat_facs) { let other_facs = compat_facs .filter(f => f != state["fac"]) .map(f => `${f}/${state["subj"]}`) .join(", "); if (other_facs.length) return "other options: " + other_facs; } return ""; } // 1. most recent faculty for this course if (state["crsn"] && cached_ycq_s.course_facs[state["subj"] + state["crsn"]]) { state["fac"] = cached_ycq_s.course_facs[state["subj"] + state["crsn"]]; add_li(warn, `assuming ${state["subj"]} ${state["crsn"]} is ${state["fac"]}/${state["subj"]} because you last used ${state["fac"]} for ${state["subj"]} ${state["crsn"]}. ${other_options()}`); } else if (cached_ycq_s.subj_facs[state["subj"]]) { // 2. most recent faculty for this subject state["fac"] = cached_ycq_s.subj_facs[state["subj"]]; add_li(warn, `assuming ${state["subj"]} means ${state["fac"]}/${state["subj"]} because you last used ${state["fac"]} for ${state["subj"]}. ${other_options()}`); } else if (compat_facs) { // 3. most recent compatible faculty if (cached_ycq_s.default_facs) for (let i = cached_ycq_s.default_facs.length - 1; i >= 0; i--) if (compat_facs.indexOf(cached_ycq_s.default_facs[i]) > -1) state["fac"] = cached_ycq_s.default_facs[i]; if (state["fac"]) { add_li(warn, `assuming ${state["subj"]} means ${state["fac"]}/${state["subj"]} because you recently used ${state["fac"]}. ${other_options()}`); } else { // 4. first compatible faculty state["fac"] = compat_facs[0]; if (compat_facs.length > 1) add_li(warn, `assuming ${state["subj"]} means ${state["fac"]}/${state["subj"]}. ${other_options()}`); else add_li(warn, `assuming ${state["subj"]} means ${state["fac"]}/${state["subj"]}`); } } else { add_li(error, "no faculty provided and unknown subject"); } } if (state["subj"] && state["crsn"] && !state["cred"]) { if (cached_ycq_s.course_creds[state["subj"] + state["crsn"]]) { state["cred"] = cached_ycq_s.course_creds[state["subj"] + state["crsn"]]; add_li(warn, `assuming ${state["subj"]} ${state["crsn"]} is ${state["cred"]} credits, because you last used ${state["cred"]} for ${state["subj"]} ${state["crsn"]}.`); } else { state["cred"] = "3.00"; add_li(warn, "assuming the number of credits is 3.00, because that's the most common number."); } } let sess = document.getElementById("sessions").value.split(" "); state["sess"] = sess[0]; state["year"] = parseInt(sess[1], 10); if (state["fac"] && state["subj"] && 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["subj"]}&cn=${state["crsn"]}&cr=${state["cred"]}&ay=${state["year"]}&ss=${state["sess"]}`; link.textContent = `${state["fac"]}/${state["subj"]} ${state["crsn"]} ${state["cred"]} (${sessions.selectedOptions[0].textContent}) | York University`; linkContainer.style.display = "block"; } else if (state["fac"] && state["subj"] && state["sess"]) { link.href = `https://w2prod.sis.yorku.ca/Apps/WebObjects/cdm.woa/wa/crsq1?faculty=${state["fac"]}&subject=${state["subj"]}&academicyear=${state["year"]}&studysession=${state["sess"]}`; link.textContent = `${state["fac"]}/${state["subj"]} Courses (${sessions.selectedOptions[0].textContent}) | York University`; linkContainer.style.display = "block"; } else { linkContainer.style.display = "none"; add_li(error, "need at least subject. see examples above"); } if (multiple.size) add_li(warn, "multiple terms for " + Array.from(multiple.values()).join(", ") + ", using last occurrence for each"); curWarn.parentNode.replaceChild(warn, curWarn); curError.parentNode.replaceChild(error, curError); document.getElementById("warning").style.display = warn.children.length ? "block" : "none"; document.getElementById("error").style.display = error.children.length ? "block" : "none"; }; input.addEventListener("input", apply); sessions.addEventListener("change", apply); document.body.addEventListener("click", function (e) { if (e.target.closest(".sample-query")) { e.preventDefault(); input.select(); // doesn't work in Firefox, oh well if (!document.execCommand || !document.execCommand("insertText", false, e.target.textContent)) input.value = e.target.textContent; apply(); input.focus(); } else if (e.target.closest(".prepend-query")) { e.preventDefault(); let r = false; let t = e.target.hasAttribute("data-query") ? e.target.getAttribute("data-query") : e.target.textContent; if (document.execCommand) { input.setSelectionRange(0, 0); r = document.execCommand("insertText", false, t + " "); } if (!r) input.value = t + " " + input.value; apply(); } }); let save_defaults = function () { let old_ycq_s_s = localStorage.ycq; let new_ycq_s = parse_ycq_s(old_ycq_s_s); if (state["fac"]) { let default_facs = new Set(new_ycq_s.default_facs); default_facs.delete(state["fac"]); default_facs.add(state["fac"]); let new_facs = Array.from(default_facs); if (new_facs.length != new_ycq_s.default_facs.length || new_facs.some((v, i) => v !== new_ycq_s.default_facs[i])) { new_ycq_s.default_facs = new_facs; } } if (state["subj"]) { if (state["fac"]) { if (state["crsn"]) new_ycq_s.course_facs[state["subj"] + state["crsn"]] = state["fac"]; new_ycq_s.subj_facs[state["subj"]] = state["fac"]; } if (state["crsn"]) { if (state["cred"]) new_ycq_s.course_creds[state["subj"] + state["crsn"]] = state["cred"]; new_ycq_s.course_subjs[state["crsn"]] = state["subj"]; } new_ycq_s.default_subj = state["subj"]; } new_ycq_s.sess = document.getElementById("sessions").value; let new_ycq_s_s = JSON.stringify(new_ycq_s); if (new_ycq_s_s != old_ycq_s_s) { cached_ycq_s = new_ycq_s; localStorage.ycq = new_ycq_s_s; } }; link.addEventListener("click", save_defaults); 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 keyboard modifiers // this way triggers popup blocker but nothing we can do about it if (navigator.userAgent.indexOf("Firefox/") > -1 && (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) { if (e.ctrlKey) { link.target = "_blank"; link.click(); link.target = ""; } else if (e.shiftKey) { // note: other methods save by link click listener // for efficiency, don't save twice save_defaults(); window.open(link.href, "_blank", "noopener,width=625,resizable,menubar,toolbar,location,personalbar,status,scrollbars"); } else { // dunno what to do link.click(); } } else { link.dispatchEvent(new MouseEvent("click", e)); } } } }); apply(); } if (document.readyState === "interactive" || document.readyState === "complete") { ready(); } else { document.addEventListener("DOMContentLoaded", ready); } }());