Skip to content

Instantly share code, notes, and snippets.

@marcos-velez
Last active January 22, 2025 23:15
Show Gist options
  • Save marcos-velez/e945b417bd5f2dede28fa11bb83a51b3 to your computer and use it in GitHub Desktop.
Save marcos-velez/e945b417bd5f2dede28fa11bb83a51b3 to your computer and use it in GitHub Desktop.
This bookmarklet generates a clean and simple UI to browse real-time PATH train arrivals. Information is refreshed every 30 seconds.
/* 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, but refer to line 12 for station codes */
/* 3. Use a meaningful (or fun!) name, like: "Simple PATH Times" or "🚊 Arrivals" */
/* 4. Drag the generated bookmarklet link to your Bookmarks Toolbar */
/* 6. Enjoy the clean, simple UI to browse PATH 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('To use this bookmarklet, you must first navigate to:\n' + u + '\n\nYou will be taken there now. Once the page loads, click the bookmarklet again.' ); w.location.href = 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 += `<tr><td class="sw"><div class="sw ci c${t.lineColor.split(',')[0]}"></div></td><td class="sw"><div class="sw ci c${t.lineColor.split(',')[1]}"></div></td><td class="sta">${t.headSign.replace(/via Hoboken/i, "(via Hoboken)")}</td><td class="at">${t.arrivalTimeMessage}</td></tr>`;});} catch { tt = '<tr><td colspan="4"><br />No results based on selected criteria</td></tr>'; } gei("tt").innerHTML = tts + tt + "</table>";};
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);};
iid = gei("iid"); if (iid && (iid.value)) { clearInterval(iid.value); }
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 = '<style> .sta {width: 250px; padding-left: 4px;} .at {width: 100px;} .sw {width: 8px;} .ci {height: 8px; border-radius: 50%;} .c4D92FB {background-color: #4D92FB;} .cFF9900 {background-color: #FF9900;} .cD93A30 {background-color: #D93A30;} .c65C100 {background-color: #65C100;} body, div, select, table, td, div, span, p {font: 9pt Consolas, Courier;} #p2 {font: italic 8pt Consolas, Courier;} table {border: 0;} td {padding: 0px;} @media only screen and (max-device-width: 480px) { .sta {width: 300px; padding-left: 4px;} td {font-size: 12pt;} }</style>';
m += '\nTrains from ' + ss + ' towards ' + sd;
m += '<br /><br />'
m += '<span id="tt"></span>';
tts = '<table>';
tts += '<tr><td colspan="3" style="width: 270px;">Train to</td><td class="at">Arrives in</td></tr>';
tts += '<tr><td colspan="3" style="width: 270px;">--------</td><td class="at">----------</td></tr>';
b.innerHTML = m + '<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>';
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