Skip to content

Instantly share code, notes, and snippets.

@marcos-velez
Last active January 21, 2025 18:39
Show Gist options
  • Save marcos-velez/76fdaf94ac7cf7ac5c8ca8c10f7c2f27 to your computer and use it in GitHub Desktop.
Save marcos-velez/76fdaf94ac7cf7ac5c8ca8c10f7c2f27 to your computer and use it in GitHub Desktop.
Bookmarklet to display a clean, simple text-based UI to browse PATH train status info
/* 1. Paste this entire gist over at https://mrcoles.com/bookmarklet/ to generate a bookmarklet */
/* 2. If desired, update the default PATH station code and heading [line #9 below] - refer to line 12 for station codes */
/* 3. Use a meaningful (or fun!) name, like: "Simple PATH Times" or "🚊 Times" */
/* 4. Drag the generated bookmarklet link to your Bookmarks Toolbar. */
/* 5. Click on the bookmarklet when on the PATH JSON feed page [https://www.panynj.gov/bin/portauthority/ridepath.json] */
/* 6. Enjoy the clean, simple UI to browse PATH train arrivals information */
function(){
sta = 'EXP'; dir = 'NY';
let da = ['NJ', 'NY'];
let sa = new Map([["NWK", [0, "Newark"]], ["HAR", [1, "Harrison"]], ["JSQ", [2, "Journal Square"]], ["GRV", [3, "Grove Street"]], ["NEW", [4, "Newport / Pavonia"]], ["EXP", [5, "Exchange Place"]], ["HOB", [6, "Hoboken"]], ["WTC", [7, "World Trade Center"]], ["CHR", [8, "Christopher Street"]], ["09S", [9, "9th Street"]], ["14S", [10, "14th Street"]], ["23S", [11, "23rd Street"]], ["33S", [12, "33rd Street"]]]);
w = window; d = w.document; b = d.body; u = 'https://www.panynj.gov/bin/portauthority/ridepath.json';
if (w.location.href.split('#')[0] != u) {alert('In order to use this bookmarklet, you must first navigate to ' + u); return; }
w.gei = function(i) { return d.getElementById(i); };
w.rtt = function() { s = gei("ss").value; h = gei("sd").selectedIndex; jt = gei("jo").textContent; tt = ""; try { jd = JSON.parse(jt); if ((jd.results[s].destinations.length == 1) && (jd.results[s].destinations[0].label == "ToNY")) { h ^= 1; } jd.results[s].destinations[h].messages.forEach(t => {tt += "<br />" + t.headSign.replace(/via Hoboken/i, "(via Hoboken)").padEnd(32).replaceAll(" ", "&nbsp;") + t.arrivalTimeMessage;});} catch { tt = "<br /><br />No results based on selected criteria"; } gei("tt").innerHTML = tt;};
w.gjd = function() { fetch(u).then(response => response.text()).then(data => {gei("jo").textContent = data; gei("p2").textContent = 'Last updated on ' + (new Date()).toLocaleString().replace(', ', ' at '); iid=gei("iid").value; if (iid) { clearInterval(iid); st(30); } rtt();}).catch(error => console.error("Encountered an unexpected error when refreshing the train data! Error details: ", error)); };
w.st = function(t) { tl = t - 1; gei('iid').value = setInterval(function () { gei('s1').textContent = tl; if (--tl < 1) { gjd(); tl = t;}}, 1000);};
o = 'option'; se = 'select';
let ss = `<${se} id="ss">`; sa.forEach((v, k) => {ss += `<${o} value="${v[0]}"` + ((k == sta) ? ' selected' : '') + `>${v[1]}</${o}>`;}); ss += `</${se}>`;
let sd = `<${se} id="sd">`; da.forEach(d => {sd += `<${o} value="${d}"` + ((d == dir) ? ' selected' : '') + `>${d}</${o}>`;}); sd += `</${se}>`;
let m = 'Trains from ' + ss + ' towards ' + sd;
m += '<br /><br />' + 'Train to'.padEnd(32).replaceAll(' ', '&nbsp;') +'Arrives in';
m += '<br />' + '--------'.padEnd(32).replaceAll(' ', '&nbsp;') +'----------';
m += '<span id="tt"></span>';
b.innerHTML = m + '<br /><br /><br /><p id="p1">Train data will be refreshed in <span id="s1">30</span> seconds, or you can <a id="a1" href="#" onclick="gjd();">refresh it now</a>.</p><p id="p2"></p><input id="iid" type="hidden" value="" /><script id="jo" type="application/json">' + (gei("jo") ? gei("jo") : b).textContent + '</script>';
f = "9pt Consolas, Courier"; gei("ss").style.font = gei("sd").style.font = b.style.font = f; gei("p2").style.font = f.replace('9pt','italic 7pt');
rtt(); st(30);
d.querySelectorAll("select").forEach(e => {e.addEventListener('change', rtt);});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment