From 763c171af03e093b5d9605afb94a0e03a2a5b1df Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Sun, 12 May 2024 16:39:39 -0700 Subject: [PATCH] Fix some more JS issues - uninitialized vars - use of == and != - throwing string literal - not using shorthand property - not using dot notation - unused variables/statements - coercion shorthands - useless template literals - function keyword with callback - logical operators instead of chains - use of `javascript:` url --- static/d3-timeline.js | 6 +- static/index.js | 149 +++++++++++++++++++++--------------------- 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/static/d3-timeline.js b/static/d3-timeline.js index fe2227c..3d2aace 100644 --- a/static/d3-timeline.js +++ b/static/d3-timeline.js @@ -198,7 +198,7 @@ export const timeline = () => { d.forEach((datum, index) => { datum.times.forEach((time, j) => { if (index === 0 && j === 0) { - originTime = time.startingTime; // store the timestamp that will serve as origin + const originTime = time.startingTime; // store the timestamp that will serve as origin time.startingTime = 0; // set the origin time.endingTime = time.endingTime - originTime; // store the relative time (millis) } else { @@ -461,7 +461,7 @@ export const timeline = () => { // set bounding rectangle height d3.select(gParent._groups[0][0]).attr("height", height); } else { - throw "height of the timeline is not set"; + throw new Error("height of the timeline is not set"); } } else { if (!height) { @@ -477,7 +477,7 @@ export const timeline = () => { try { width = gParentItem.attr("width"); if (!width) { - throw "width of the timeline is not set. As of Firefox 27, timeline().with(x) needs to be explicitly set in order to render"; + throw new Error("width of the timeline is not set. As of Firefox 27, timeline().with(x) needs to be explicitly set in order to render"); } } catch (err) { console.log(err); diff --git a/static/index.js b/static/index.js index 5ac96f1..b4f9a72 100644 --- a/static/index.js +++ b/static/index.js @@ -123,7 +123,7 @@ function showMessage(heading, text, theme) { positionClass: "nfc-top-right", onclick: false, showDuration: 3500, - theme: theme, + theme, })({ title: heading, message: text, @@ -131,7 +131,7 @@ function showMessage(heading, text, theme) { } function hideMessage() { - if (messageHidden == true) return; + if (messageHidden === true) return; messageHidden = true; elements.messageCard.hidden = true; } @@ -158,7 +158,7 @@ function resetState() { async function fetchApiData(request, body, method = "GET") { const fetchOptions = { - method: method, + method, body: JSON.stringify(body), headers: { "Content-Type": "application/json" }, }; @@ -167,7 +167,7 @@ async function fetchApiData(request, body, method = "GET") { const responseJson = await response.json(); if (!response.ok) { let msg = response.statusText; - if (responseJson && responseJson.detail) msg += " " + JSON.stringify(responseJson.detail); + if (responseJson?.detail) msg += " " + JSON.stringify(responseJson.detail); throw new Error(msg); } return responseJson; @@ -214,7 +214,7 @@ function escapeRegex(string) { function autoComplete(input) { const value = input.value.trim(); - if (value != "" && !isValidLocation(value)) { + if (value !== "" && !isValidLocation(value)) { let locationName = value; if (value in locations) { locationName = locations[value]; @@ -228,7 +228,7 @@ function autoComplete(input) { } } } - if (locationName != value) { + if (locationName !== value) { input.value = locationName; onInput(); } @@ -239,10 +239,10 @@ function getOptions(excludeDefaults) { let options = {}; for (const input of elements.inputForm.getElementsByTagName("input")) { const value = inputValue(input); - if (excludeDefaults == true && value == input.default) continue; + if (excludeDefaults === true && value === input.default) continue; options[input.id] = value; } - if (currentPlan) options["hash"] = currentPlan.hash; + if (currentPlan) options.hash = currentPlan.hash; return options; } @@ -251,8 +251,8 @@ async function applyOptions(options) { let hash = ""; for (const o in options) { const value = options[o]; - if (value == undefined) continue; - if (o == "hash") { + if (value === undefined) continue; + if (o === "hash") { // will apply later below hash = value; continue; @@ -263,7 +263,7 @@ async function applyOptions(options) { continue; } const currentValue = inputValue(element); - if (`${value}` == `${currentValue}`) continue; + if (`${value}` === `${currentValue}`) continue; switch (element.type) { case "checkbox": @@ -291,9 +291,9 @@ function optionsToUrl(options, url) { url.hash = ""; for (const key in options) { const value = options[key]; - if (value != undefined && value != null) { - if (key == "hash") { - if (value != "") url.hash = value; + if (value !== undefined && value !== null) { + if (key === "hash") { + if (value !== "") url.hash = value; } else { url.searchParams.append(key, value); } @@ -310,7 +310,7 @@ function urlToOptions(url) { let options = {}; for (const param of url.searchParams.entries()) options[param[0]] = param[1]; - if (url.hash != "") options["hash"] = url.hash; + if (url.hash !== "") options.hash = url.hash; return options; } @@ -331,11 +331,11 @@ function trimEnd(str, ch) { function saveHistory(options, hash) { if (!options) options = getOptions(true); - if (hash) options["hash"] = hash; + if (hash) options.hash = hash; const url = optionsToUrl(options); // don't push duplicate states - if (trimEnd(url.href, "#") == trimEnd(window.location.href, "#")) { + if (trimEnd(url.href, "#") === trimEnd(window.location.href, "#")) { return; } @@ -344,8 +344,8 @@ function saveHistory(options, hash) { function validatePlans(options) { if (!plans || !plans.options) return false; - for (const k in options) if (k != "hash" && options[k] != plans.options[k]) return false; - for (const k in plans.options) if (k != "hash" && options[k] != plans.options[k]) return false; + for (const k in options) if (k !== "hash" && options[k] !== plans.options[k]) return false; + for (const k in plans.options) if (k !== "hash" && options[k] !== plans.options[k]) return false; return true; } @@ -367,9 +367,9 @@ function showElements(elements) { export async function goto(hash, clickEvent) { menuClose(); if (!hash) hash = ""; - if (hash.length > 0 && hash[0] == "#") hash = hash.substring(1); + if (hash.length > 0 && hash[0] === "#") hash = hash.substring(1); - if (clickEvent && clickEvent.ctrlKey) { + if (clickEvent?.ctrlKey) { let url = new URL(window.location); url.hash = hash; window.open(url, "_blank").focus(); @@ -380,13 +380,13 @@ export async function goto(hash, clickEvent) { const options = getOptions(); if (!validatePlans(options)) plans = null; - if (hash != "") { - if (plans == null) await fetchRoutes(); - if (plans != null) { - if (hash == "routes") { + if (hash !== "") { + if (plans === null) await fetchRoutes(); + if (plans !== null) { + if (hash === "routes") { /* pass */ } else { - const plan = plans.find((p) => p.hash == hash); + const plan = plans.find((p) => p.hash === hash); if (plan) { onPlanSelected(plan.id); } else { @@ -397,20 +397,20 @@ export async function goto(hash, clickEvent) { } } - if (plans && plans.length == 0) plans = null; - if (plans == null) hash = ""; - if (hash == "") currentPlan = null; + if (plans && plans.length === 0) plans = null; + if (plans === null) hash = ""; + if (hash === "") currentPlan = null; // mark selected row in routes table for (const row of elements.routesTable.children) { row.classList.remove("selected-row"); - if (currentPlan && row.plan == currentPlan) row.classList.add("selected-row"); + if (currentPlan && row.plan === currentPlan) row.classList.add("selected-row"); } - if (hash == "") { + if (hash === "") { showElements([elements.inputForm]); document.title = "Ferry Planner"; - } else if (hash == "routes") { + } else if (hash === "routes") { const depart_time = new Date(plans[0].depart_time.substring(0, 16)); const cardHeader = document.createElement("div"); cardHeader.className = "card-header"; @@ -443,11 +443,11 @@ export async function goto(hash, clickEvent) { ).toDateString()} at ${timeToString(currentPlan.depart_time)}`; } - /* elements.inputForm.hidden = hash != "" && hash != "routes"; - elements.routesCard.hidden = hash != "routes"; - elements.scheduleCard.hidden = currentPlan == null || hash == "routes"; + /* elements.inputForm.hidden = hash !== "" && hash !== "routes"; + elements.routesCard.hidden = hash !== "routes"; + elements.scheduleCard.hidden = currentPlan === null || hash === "routes"; */ - //if (window.location.hash != hash) { + //if (window.location.hash !== hash) { // window.location.hash = hash; // } saveHistory(null, hash); @@ -466,7 +466,7 @@ async function getRoutePlans() { plan.id = i + 1; let via = new Set(); for (const s of plan.segments) { - if (s.connection.type == "FERRY") { + if (s.connection.type === "FERRY") { let lg = s.connection.destination.land_group; if (lg) { const pos = lg.indexOf(" ("); @@ -496,11 +496,11 @@ async function getRoutePlans() { function isValidLocation(name) { name = name.trim(); - return name != " " && name in locationsToId; + return name !== " " && name in locationsToId; } export async function submit() { - goto("routes"); + await goto("routes"); } async function fetchRoutes() { @@ -509,7 +509,7 @@ async function fetchRoutes() { saveHistory(); if (!isValidLocation(elements.inputOrigin.value)) showMessage("", "Please select start location", "warning"); else if (!isValidLocation(elements.inputDestination.value)) showWarning("Please select destination location"); - else if (elements.inputOrigin.value == elements.inputDestination.value) { + else if (elements.inputOrigin.value === elements.inputDestination.value) { showError("Start and destination location cannot be the same"); } else { try { @@ -518,7 +518,7 @@ async function fetchRoutes() { //for (const element of elements.inputs) element.disabled = true; plans = await getRoutePlans(); if (!plans) showError("Failed to fetch schedule information"); - else if (plans.length == 0) { + else if (plans.length === 0) { showMessage("", "No itineraries found. Try select another date and/or locations.", "warning"); plans = null; } else { @@ -564,7 +564,7 @@ function timeToString(time, roundSeconds = true) { // hours -= 12; // ampm = "pm"; // } - // if (hours == 0) hours = 12; + // if (hours === 0) hours = 12; // const timeString = hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ampm; // return timeString; } @@ -592,14 +592,14 @@ function columnsCount() { function updateRoutesTable() { if (elements.tabRoutesTable.hidden) return; - if (tabsState.routesTableSort == currentSort && tabsState.columnsCount == columnsCount()) return; + if (tabsState.routesTableSort === currentSort && tabsState.columnsCount === columnsCount()) return; tabsState.routesTableSort = currentSort; tabsState.columnsCount = columnsCount(); let headerRowHtml = ""; let c = 0; for (const k in columns) { - if (c++ == tabsState.columnsCount) break; + if (c++ === tabsState.columnsCount) break; headerRowHtml += `${k}`; } elements.tabRoutesTableHeaderRow.innerHTML = headerRowHtml; @@ -611,7 +611,7 @@ function updateRoutesTable() { const plan = plans[i]; let tr = document.createElement("tr"); - tr.setAttribute("onclick", `javascript: exports.goto(this.plan.hash, event);`); + tr.setAttribute("onclick", "exports.goto(this.plan.hash, event);"); tr.classList.add("routes-table-row"); tr.plan = plan; if (new Date(plan.depart_time) < Date.now() - 60000) { @@ -668,17 +668,17 @@ export function updateTimelines() { return; } - if (elements.tabRoutesTimelines.hidden || elements.tabRoutesTimelines.clientWidth == 0) return; + if (elements.tabRoutesTimelines.hidden || elements.tabRoutesTimelines.clientWidth === 0) return; const currentColoring = document.getElementById("color-option").value; if ( - tabsState.timelinesSort == currentSort && - tabsState.timelinesColoring == currentColoring && - tabsState.timelineWidth == elements.tabRoutesTimelines.clientWidth + tabsState.timelinesSort === currentSort && + tabsState.timelinesColoring === currentColoring && + tabsState.timelineWidth === elements.tabRoutesTimelines.clientWidth ) return; tabsState.timelinesSort = currentSort; tabsState.timelinesColoring = currentColoring; - tabsState.timelineWidth == elements.tabRoutesTimelines.clientWidth; + tabsState.timelineWidth = elements.tabRoutesTimelines.clientWidth; d3.select(elements.timeline).select("svg").remove(); let chartRows = []; let coloringKeys = new Set(); @@ -692,13 +692,13 @@ export function updateTimelines() { for (const s of plan.segments) { for (const t of s.times) { let label = ""; - const segmentType = t.type == "TRAVEL" ? s.connection.type : t.type; + const segmentType = t.type === "TRAVEL" ? s.connection.type : t.type; const activityInfo = activitiesInfo[segmentType]; - if (location == null || t.type == "TRAVEL") { - if (location != s.connection.destination) { + if (location === null || t.type === "TRAVEL") { + if (location !== s.connection.destination) { location = s.connection.destination; landGroup = location.land_group; - if (landGroup == undefined) { + if (landGroup === undefined) { landGroup = null; if (location.address.indexOf("Island") > 0 || location.name.indexOf("Island") > 0) landGroup = "Islands"; } @@ -706,14 +706,14 @@ export function updateTimelines() { landGroup = landGroup.substring(0, landGroup.indexOf("(")).trim(); } - // label = location.id.length == 3 ? location.id : location.name; + // label = location.id.length === 3 ? location.id : location.name; } } if (activityInfo.icon) label = `${activityInfo.icon}` + label; const t2 = { description: t.description, - segmentType: segmentType, + segmentType, startingTime: new Date(t.start).getTime(), endingTime: new Date(t.end).getTime(), }; @@ -721,12 +721,12 @@ export function updateTimelines() { t2.label = ""; // just a placeholder now, will be replaced with _label later t2._label = label; } - const colorKey = currentColoring == "activity" ? segmentType : landGroup; - if (colorKey != null) { + const colorKey = currentColoring === "activity" ? segmentType : landGroup; + if (colorKey !== null) { coloringKeys.add(colorKey); t2._color = colorKey; } - if (t.end == t.start) { + if (t.end === t.start) { t2.display = "circle"; t2._label = ""; // don't show labels for start/finish chartRow.times.splice(0, 0, t2); @@ -739,7 +739,7 @@ export function updateTimelines() { } let colorScale; - if (currentColoring == "activity") { + if (currentColoring === "activity") { colorScale = d3.scaleOrdinal().range(Object.values(activityColorsMap)).domain(Object.keys(activityColorsMap)); } else { colorScale = d3.scaleOrdinal().range(d3.scaleOrdinal(d3.schemeAccent).range()).domain(Array.from(coloringKeys)); @@ -774,8 +774,7 @@ export function updateTimelines() { .style("cursor", "default") ._groups[0].forEach((element) => { assignTooltip(element); - const d = element.__data__; - if (d && d._label) element.innerHTML = d._label; + if (element.__data__?._label) element.innerHTML = element.__data__._label; // if (element.getClientRects()[0].width < element.textLength.baseVal.value) { // element.innerHTML = ''; // } @@ -793,9 +792,9 @@ function updateLegend(chart, coloringKeys, currentColoring, legendElement) { for (let i = 0; i < colorsRange.length && i < coloringKeys.length; i++) { let n = colorsDomain.indexOf(coloringKeys[i]); let c = colorsRange[n]; - legend += `  `; + legend += "  "; legend += `
 
 `; - if (currentColoring == "activity") { + if (currentColoring === "activity") { const activityInfo = activitiesInfo[coloringKeys[i]]; legend += `${activityInfo.icon}`; } @@ -808,10 +807,10 @@ function assignTooltip(element) { const tooltip = d3.select(elements.tooltip); element.onmousemove = (event) => { let n = event.target; - if (n.nodeName == "tspan") n = n.parentNode; - if (n.nodeName == "text") n = n.parentNode; + if (n.nodeName === "tspan") n = n.parentNode; + if (n.nodeName === "text") n = n.parentNode; const rect = n.getBoundingClientRect(); - tooltip.style("left", rect.x + window.scrollX + "px").style("top", rect.bottom + +window.scrollY + 3 + "px"); + tooltip.style("left", rect.x + window.scrollX + "px").style("top", rect.bottom + window.scrollY + 3 + "px"); }; element.onmouseout = (event) => { tooltip.transition().duration(100).style("opacity", 0); @@ -833,7 +832,7 @@ function assignTooltip(element) { function onPlanSelected(id) { elements.scheduleCard.hidden = false; - const plan = plans.find((p) => p.id == id); + const plan = plans.find((p) => p.id === id); currentPlan = plan; // clear table @@ -874,12 +873,12 @@ function onPlanSelected(id) { tr.appendChild(td); let desc = t.description; - if (s.schedule_url && t.type == "TRAVEL" && t.start != t.end) { + if (s.schedule_url && t.type === "TRAVEL" && t.start !== t.end) { desc += ` Schedule`; } td = document.createElement("td"); if (t.description.includes("Ferry")) { - desc = ` ` + desc; + desc = ` ${desc}`; } td.innerHTML = desc; tr.appendChild(td); @@ -895,7 +894,7 @@ function onPlanSelected(id) { } export function sortPlans(sortBy) { - if (sortBy == currentSort) return; + if (sortBy === currentSort) return; plans.sort((a, b) => { if (a[sortBy] >= b[sortBy]) return 1; return -1; @@ -912,7 +911,7 @@ function updateTabsData() { function showTab(id) { if (currentTab) { - if (currentTab.id == id) return; + if (currentTab.id === id) return; currentTab.hidden = true; } currentTab = document.getElementById(id); @@ -954,7 +953,7 @@ export function onShare() { window.navigator.share(data).then(); } catch (error) { if (!navigator.clipboard) { - showError(`Cannot copy link to clipboard`); + showError("Cannot copy link to clipboard"); } else { navigator.clipboard .writeText(window.location.href) @@ -998,7 +997,7 @@ function inputValue(input) { function outputsize(event) { const target = event[0].target; - if (target.clientWidth != 0) { + if (target.clientWidth !== 0) { window.setTimeout(updateTabsData, 0); } } @@ -1056,7 +1055,7 @@ function init() { if (event.code === "Enter") submit(); }); elements.timelineSwitch.addEventListener("change", () => { - window.setTimeout(function () { + window.setTimeout(() => { showTab(elements.timelineSwitch.checked ? "tab-routes-timeline" : "tab-routes-table"); }, 0); });