v1
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
const BASE_MAP_URL = new URL("/public-transport/lines/", document.baseURI)
|
||||
|
||||
const DAY_IN_MS = 8.64e+7;
|
||||
|
||||
async function waitForElement(iframe, selector, options = {
|
||||
timeout: 15000,
|
||||
multiple: false
|
||||
}){
|
||||
let timeout = options?.timeout || 15000
|
||||
let multiple = options?.multiple || false
|
||||
|
||||
let el = null;
|
||||
let startDate = Date.now()
|
||||
|
||||
if(multiple){
|
||||
while(!(
|
||||
el = iframe.contentWindow.document.querySelectorAll(selector)
|
||||
)?.length){
|
||||
await new Promise(res => setTimeout(res, 500))
|
||||
if(Date.now() > startDate+timeout) {
|
||||
throw new Error("Element search timeout")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while((
|
||||
el = iframe.contentWindow.document.querySelector(selector)
|
||||
) == null){
|
||||
await new Promise(res => setTimeout(res, 500))
|
||||
if(Date.now() > startDate+timeout) {
|
||||
throw new Error("Element search timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
async function spawnIframe(url){
|
||||
/** @type {HTMLIFrameElement} */
|
||||
let iframe = document.createElement("iframe")
|
||||
|
||||
let container = document.createElement("div")
|
||||
container.classList.add("thinking")
|
||||
|
||||
container.style.left = Math.round(Math.random()*(window.innerWidth-400))+"px"
|
||||
container.style.top = Math.round(Math.random()*(window.innerHeight-350))+"px"
|
||||
|
||||
let img = document.createElement("img")
|
||||
img.src = "/lol/math.gif"
|
||||
container.append(img)
|
||||
|
||||
container.append(iframe)
|
||||
|
||||
document.body.append(container)
|
||||
|
||||
await navigateIframe(iframe, url)
|
||||
|
||||
return iframe
|
||||
}
|
||||
|
||||
async function navigateIframe(iframe, url){
|
||||
let prom = new Promise(res => iframe.addEventListener("load", res(), {once: true}))
|
||||
iframe.src = new URL(url, BASE_MAP_URL).toString()
|
||||
await prom
|
||||
|
||||
return iframe
|
||||
}
|
||||
|
||||
async function getLineDetails(iframe){
|
||||
let lineElement = await waitForElement(iframe, `[class|="content"] [class|="pictoAndDirection"]`)
|
||||
let linePictoElement = await waitForElement(iframe, `[class|="content"] [class|="linePictoSvg"]`)
|
||||
let directionTextElement = await waitForElement(iframe, `[class|="content"] [class|="directionText"]`)
|
||||
|
||||
let line = {
|
||||
displayName: lineElement.getAttribute("aria-label"),
|
||||
picto: linePictoElement.src,
|
||||
name: linePictoElement.getAttribute("aria-label"),
|
||||
direction: directionTextElement.innerText,
|
||||
timetable: null
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
async function getStopTimeTable(iframe, stop_description, amount=1){
|
||||
let timetable = []
|
||||
for(let dayOffset = 0; dayOffset<amount; dayOffset++){
|
||||
|
||||
let now = new Date(Date.now()+(dayOffset*DAY_IN_MS))
|
||||
await navigateIframe(iframe, "./"+stop_description+`/timetable?date=${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2, "0")}-${(now.getDate()).toString().padStart(2, "0")}`)
|
||||
|
||||
let timeTableElList = await waitForElement(iframe, `[class|="content"] [class|="timeTableContainer"] table`, {multiple: true});
|
||||
for(let timeTableEl of timeTableElList){
|
||||
let trancheHoraires = timeTableEl.querySelectorAll("thead tr:first-child th")
|
||||
for(let i = 0; i< trancheHoraires.length; i++){
|
||||
let el = trancheHoraires[i]
|
||||
let hours = parseInt(el.innerText.trim())
|
||||
if(Number.isFinite(hours)){
|
||||
let allHoraires = timeTableEl.querySelectorAll(`tbody tr td:nth-child(${i+1})`)
|
||||
for(let hel of allHoraires){
|
||||
let minutes = parseInt(hel.innerText.trim())
|
||||
if(Number.isFinite(minutes)){
|
||||
timetable.push({
|
||||
displayHours: el.innerText,
|
||||
displayMinutes: hel.innerText,
|
||||
displayTime: `${el.innerText}${hel.innerText}`,
|
||||
time: new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
hours,
|
||||
minutes
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return timetable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next Bus/Metro/Tram passage of given stop description
|
||||
* @param {String} stop_description Stop description string
|
||||
*
|
||||
* To get stop description go to https://carte-interactive.tcl.fr/public-transport/lines/
|
||||
* then find your line and your stop in this line.
|
||||
* Your URL must look like something like this
|
||||
* https://carte-interactive.tcl.fr/public-transport/lines/line:SYTNEX:C/forward/stop_point:SYTNEX:10787
|
||||
* Stop description is everything after "lines/" ("line:SYTNEX:C/forward/stop_point:SYTNEX:10787" in example above)
|
||||
*/
|
||||
export async function getNextPassage(stop_description, options = {
|
||||
timetable: +2 // Today and tomorrow
|
||||
}){
|
||||
let iframe = await spawnIframe("./"+stop_description)
|
||||
|
||||
let nextPassageElement = await waitForElement(iframe, `[class|="content"] [class|="stopCard"] [class|="schedule"]`);
|
||||
let stopName = await waitForElement(iframe, `[class|="content"] [class|="stopCard"] [class|="nameStop"]`);
|
||||
|
||||
let line = await getLineDetails(iframe, stop_description)
|
||||
|
||||
let timetable = []
|
||||
let timeTableAmount = options?.timetable || +2;
|
||||
if(timeTableAmount){
|
||||
timetable = await getStopTimeTable(iframe, stop_description, timeTableAmount)
|
||||
} else {
|
||||
timetable = null
|
||||
}
|
||||
line.timetable = timetable
|
||||
|
||||
let nextPassage = {
|
||||
line,
|
||||
displayName: stopName.innerText,
|
||||
displayTime: nextPassageElement.innerText
|
||||
}
|
||||
|
||||
{
|
||||
let timeText = nextPassageElement.innerText.trim();
|
||||
let time;
|
||||
if(timeText.endsWith("min")){
|
||||
time = new Date(Date.now() + parseInt(timeText)*60000)
|
||||
} else {
|
||||
let match = timeText.match(/^(\d+):(\d+)/)
|
||||
if(match){
|
||||
let now = new Date()
|
||||
time = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
parseInt(match[1]),
|
||||
parseInt(match[2])
|
||||
)
|
||||
|
||||
if(time.getTime() < Date.now()){
|
||||
time.setTime(time.getTime() + DAY_IN_MS)
|
||||
}
|
||||
}
|
||||
}
|
||||
nextPassage.time = time
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
iframe.parentElement.remove()
|
||||
}, Math.random()*1000)
|
||||
|
||||
return nextPassage
|
||||
}
|
||||
Reference in New Issue
Block a user