Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed621b7ce2 | |||
| 0e9150b5ce | |||
| 6f591c10d5 | |||
| a15dccf3c2 | |||
| f82fe1a524 | |||
| 0b4852c582 | |||
| 37ff82a6ef | |||
| 5d1236dd87 | |||
| 4642ca0115 | |||
| 0a3d7baf82 | |||
| 885a96dced | |||
| f437fb8c5a | |||
| 366d0dd454 | |||
| 80b1b2b0cc | |||
| 269a651325 | |||
| 6386a3420e | |||
| eecb0ed95b | |||
| 99bb3b2981 |
@@ -19,8 +19,8 @@ android {
|
||||
applicationId "ru.ua1zbe.bus127"
|
||||
minSdk 26
|
||||
targetSdk 34
|
||||
versionCode 3
|
||||
versionName "2.1"
|
||||
versionCode 8
|
||||
versionName "2.6"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
color:var(--txt);min-height:100vh;padding:24px 16px 48px}
|
||||
.wrap{max-width:760px;margin:0 auto}
|
||||
header{text-align:center;margin-bottom:18px}
|
||||
.route{font-size:12px;letter-spacing:.14em;color:var(--muted);text-transform:uppercase}
|
||||
h1{font-size:clamp(22px,4.5vw,30px);margin:6px 0 4px;font-weight:700}
|
||||
h1{font-size:clamp(22px,4.5vw,30px);margin:0 0 4px;font-weight:700}
|
||||
.sub{color:var(--muted);font-size:14px}
|
||||
.clock{font-size:22px;font-weight:600;margin-top:10px;color:var(--accent)}
|
||||
.clock small{color:var(--muted);font-weight:400;font-size:12px}
|
||||
@@ -35,6 +34,7 @@
|
||||
.where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px}
|
||||
|
||||
.dir{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
||||
.dir .card:only-child{grid-column:1 / -1} /* одна карточка — во всю ширину */
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
|
||||
.card h3{margin:0 0 4px;font-size:15px;font-weight:700}
|
||||
.card h3 .arr{color:var(--accent)}
|
||||
@@ -58,6 +58,15 @@
|
||||
|
||||
.tablecard{background:var(--card);border:1px solid var(--line);border-radius:14px;
|
||||
overflow:hidden;display:none;margin-bottom:8px}
|
||||
/* переключатель типа дня в полном расписании */
|
||||
.days{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;padding:12px 12px 10px;
|
||||
border-bottom:1px solid var(--line)}
|
||||
.days button{padding:9px 4px;border-radius:10px;border:1px solid var(--line);
|
||||
background:var(--card2);color:var(--muted);font-size:13px;font-weight:600;
|
||||
cursor:pointer;transition:.15s;font-family:inherit}
|
||||
.days button:hover{border-color:var(--accent2);color:var(--txt)}
|
||||
.days button.active{background:linear-gradient(135deg,var(--accent2),var(--accent));
|
||||
color:#04101c;border-color:transparent}
|
||||
.thead{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;background:var(--card2);
|
||||
font-size:11px;color:var(--muted);text-align:center}
|
||||
.thead div{padding:11px 6px;border-left:1px solid var(--line)}
|
||||
@@ -76,7 +85,6 @@
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<div class="route">Мурманская область · регулярный маршрут</div>
|
||||
<h1>Автобус №127</h1>
|
||||
<div class="sub">Кандалакша — Полярные Зори — Зашеек</div>
|
||||
<div class="clock" id="clock">--:--:-- <small>МСК</small></div>
|
||||
@@ -95,6 +103,11 @@
|
||||
|
||||
<button class="fullbtn" id="fullBtn">Полное расписание <span class="chev">▾</span></button>
|
||||
<section class="tablecard" id="fullTable">
|
||||
<div class="days" id="dayPick">
|
||||
<button data-day="weekday" class="active">Будни</button>
|
||||
<button data-day="saturday">Суббота</button>
|
||||
<button data-day="sunday">Воскресенье</button>
|
||||
</div>
|
||||
<div class="thead">
|
||||
<div><b>Кандалакша</b>отпр.</div>
|
||||
<div><b>Полярные Зори</b>→ Зашеек</div>
|
||||
@@ -108,7 +121,19 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const SCHEDULE = [
|
||||
// Расписание по типам дней недели.
|
||||
// Колонки: отправление из Кандалакши · Полярные Зори (в Зашеек) ·
|
||||
// прибытие в Зашеек · Полярные Зори (обратно).
|
||||
//
|
||||
// Будни — полная таблица маршрута. По субботе и воскресенью в репозитории
|
||||
// хранились только отправления из Полярных Зорь и прибытия в Зашеек
|
||||
// (списки в app/src/main/java/ru/ua1zbe/bus127/MainActivity.kt), поэтому
|
||||
// первая и четвёртая колонки восстановлены по тем же правилам, что и для
|
||||
// будней, и требуют сверки с официальным расписанием:
|
||||
// кол0 = кол1 − 50 мин (Кандалакша → Полярные Зори занимает ровно 50 мин)
|
||||
// кол3 = кол2 + 20 мин (так разность кол3−кол2 выглядит в 8 строках из 13)
|
||||
const SCHEDULES = {
|
||||
weekday: [
|
||||
["05:20","06:20","06:30","06:50"],
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["07:40","08:30","09:00","09:20"],
|
||||
@@ -122,19 +147,57 @@ const SCHEDULE = [
|
||||
["16:50","17:40","18:10","18:35"],
|
||||
["18:20","19:10","19:40","20:05"],
|
||||
["20:20","21:10","21:40","22:05"]
|
||||
];
|
||||
],
|
||||
saturday: [
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["07:30","08:20","08:45","09:05"],
|
||||
["08:45","09:35","10:05","10:25"],
|
||||
["10:10","11:00","11:30","11:50"],
|
||||
["12:30","13:20","13:50","14:10"],
|
||||
["14:00","14:50","15:45","16:05"],
|
||||
["15:30","16:20","16:45","17:05"],
|
||||
["17:00","17:50","18:20","18:40"],
|
||||
["18:10","19:00","19:30","19:50"],
|
||||
["20:20","21:10","21:40","22:00"]
|
||||
],
|
||||
sunday: [
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["08:45","09:35","10:05","10:25"],
|
||||
["12:30","13:20","13:50","14:10"],
|
||||
["14:00","14:50","15:25","15:45"],
|
||||
["16:50","17:40","18:10","18:30"],
|
||||
["20:20","21:10","21:40","22:00"]
|
||||
]
|
||||
};
|
||||
const DAY_KEYS = ["weekday","saturday","sunday"];
|
||||
const DAY_TITLES = {weekday:"Будни", saturday:"Суббота", sunday:"Воскресенье"};
|
||||
|
||||
const TRIPS_SHOWN = 5; // сколько ближайших рейсов показывать в карточке
|
||||
|
||||
// Время в пути Полярные Зори ↔ Кандалакша. В таблице расписания его нет:
|
||||
// в 12 строках из 13 Кандалакша → ПЗ занимает ровно 50 минут
|
||||
// (в первой строке 60 — по всей видимости опечатка).
|
||||
const RIDE_PZ_KAND = 50;
|
||||
|
||||
// arrs — список прибытий: {label, col} — время из колонки таблицы,
|
||||
// {label, offset} — «время в пути» от отправления (если col не задан).
|
||||
const DIRECTIONS = {
|
||||
// От Кандалакши и Зашейка все рейсы идут через Полярные Зори — одна карточка
|
||||
"Кандалакша": [
|
||||
{to:"Полярные Зори", dep:0, arr:1, via:"через 1 час"},
|
||||
{to:"Зашеек", dep:0, arr:2, via:"через Полярные Зори"}
|
||||
{to:"Полярные Зори, Зашеек", dep:0, via:"через Полярные Зори",
|
||||
arrs:[{label:"Полярные Зори", col:1},
|
||||
{label:"Зашеек", col:2}]}
|
||||
],
|
||||
"Полярные Зори": [
|
||||
{to:"Зашеек", dep:1, arr:2, via:"≈ 30 мин в пути"},
|
||||
{to:"Кандалакша", dep:3, arr:null, via:"≈ 1 час в пути"}
|
||||
{to:"Зашеек", dep:1, via:"≈ 30 мин в пути",
|
||||
arrs:[{label:"Зашеек", col:2}]},
|
||||
{to:"Кандалакша", dep:3, via:"≈ 50 мин в пути",
|
||||
arrs:[{label:"Кандалакша", offset:RIDE_PZ_KAND}]}
|
||||
],
|
||||
"Зашеек": [
|
||||
{to:"Полярные Зори", dep:2, arr:3, via:"≈ 20 мин в пути"},
|
||||
{to:"Кандалакша", dep:2, arr:null, via:"через Полярные Зори"}
|
||||
{to:"Полярные Зори, Кандалакша", dep:2, via:"через Полярные Зори",
|
||||
arrs:[{label:"Полярные Зори", col:3},
|
||||
{label:"Кандалакша", col:3, offset:RIDE_PZ_KAND}]}
|
||||
]
|
||||
};
|
||||
const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"];
|
||||
@@ -173,6 +236,12 @@ function nowMs(){
|
||||
second: d.getUTCSeconds(), day: d.getUTCDay() };
|
||||
}
|
||||
|
||||
function fmtHM(m){
|
||||
const p = n=>String(n).padStart(2,"0");
|
||||
m = ((m % 1440) + 1440) % 1440; // на случай перехода через полночь
|
||||
return `${p(Math.floor(m/60))}:${p(m%60)}`;
|
||||
}
|
||||
|
||||
function fmtWait(m){
|
||||
if(m <= 0) return "отправление сейчас";
|
||||
const h = Math.floor(m/60), mm = m%60;
|
||||
@@ -183,24 +252,38 @@ function fmtWait(m){
|
||||
|
||||
let currentStop = "Полярные Зори";
|
||||
|
||||
// Date#getDay(): 0 — воскресенье, 6 — суббота
|
||||
function dayKeyFor(dow){ return dow===0 ? "sunday" : dow===6 ? "saturday" : "weekday"; }
|
||||
|
||||
function sched(){ return SCHEDULES[activeDay]; }
|
||||
|
||||
function render(){
|
||||
const now = nowMs();
|
||||
const curMin = now.hour*60 + now.minute;
|
||||
const dirs = DIRECTIONS[currentStop];
|
||||
const data = sched();
|
||||
document.getElementById("dirBox").innerHTML = dirs.map(d=>{
|
||||
const upcoming = SCHEDULE
|
||||
.map((r,ri)=>({dep:toMin(r[d.dep]), arr:d.arr!==null?r[d.arr]:null, ri}))
|
||||
const upcoming = data
|
||||
.map((r,ri)=>{
|
||||
const dep = toMin(r[d.dep]);
|
||||
const arrs = d.arrs.map(a=>{
|
||||
const base = a.col!==undefined ? toMin(r[a.col]) : dep;
|
||||
return {label:a.label, t:fmtHM(base + (a.offset||0))};
|
||||
});
|
||||
return {dep, arrs, ri};
|
||||
})
|
||||
.filter(x=>x.dep >= curMin)
|
||||
.sort((a,b)=>a.dep-b.dep)
|
||||
.slice(0,3)
|
||||
.slice(0,TRIPS_SHOWN)
|
||||
.map(x=>{
|
||||
const w = x.dep - curMin;
|
||||
const arr = x.arr ? `<small>прибытие ${x.arr}</small>` : "";
|
||||
// .trip .t small имеет display:block, поэтому каждый <small> — своя строка
|
||||
const arr = x.arrs.map(a=>`<small>прибытие ${a.label} ${a.t}</small>`).join("");
|
||||
return `<div class="trip ${w<15?'soon':''}">
|
||||
<span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span>
|
||||
<span class="t">${data[x.ri][d.dep]}${arr}</span>
|
||||
<span class="w">${fmtWait(w)}</span>
|
||||
</div>`;
|
||||
}).join("") || `<div class="empty">на сегодня всё — первый завтра в ${SCHEDULE[0][d.dep]}</div>`;
|
||||
}).join("") || `<div class="empty">на сегодня всё — первый завтра в ${data[0][d.dep]}</div>`;
|
||||
return `<div class="card">
|
||||
<h3>→ <span class="arr">${d.to}</span></h3>
|
||||
<div class="via">${d.via}</div>
|
||||
@@ -210,7 +293,7 @@ function render(){
|
||||
}
|
||||
|
||||
function renderTable(){
|
||||
document.getElementById("tableBody").innerHTML = SCHEDULE.map(row =>
|
||||
document.getElementById("tableBody").innerHTML = sched().map(row =>
|
||||
`<div class="row"><div>${row[0]}</div><div>${row[1]}</div><div>${row[2]}</div><div>${row[3]}</div></div>`
|
||||
).join("");
|
||||
}
|
||||
@@ -220,6 +303,15 @@ function renderDay(){
|
||||
document.getElementById("dayBadge").innerHTML = `Сегодня <b>${DAYS[dow]}</b>`;
|
||||
}
|
||||
|
||||
// Подсветка выбранного типа дня и перерисовка расписания
|
||||
function selectDay(key){
|
||||
activeDay = key;
|
||||
document.querySelectorAll("#dayPick button").forEach(b=>
|
||||
b.classList.toggle("active", b.dataset.day === key));
|
||||
render();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function tickClock(){
|
||||
const d = nowMs();
|
||||
const p = n=>String(n).padStart(2,"0");
|
||||
@@ -238,6 +330,10 @@ document.querySelectorAll(".pick").forEach(btn=>{
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("#dayPick button").forEach(btn=>{
|
||||
btn.addEventListener("click",()=>selectDay(btn.dataset.day));
|
||||
});
|
||||
|
||||
document.getElementById("fullBtn").addEventListener("click",()=>{
|
||||
const t = document.getElementById("fullTable");
|
||||
const b = document.getElementById("fullBtn");
|
||||
@@ -246,7 +342,9 @@ document.getElementById("fullBtn").addEventListener("click",()=>{
|
||||
b.classList.toggle("open", !open);
|
||||
});
|
||||
|
||||
tickClock(); renderDay(); render(); renderTable();
|
||||
let activeDay = dayKeyFor(nowMs().day); // по умолчанию — реальный день недели
|
||||
selectDay(activeDay);
|
||||
tickClock(); renderDay();
|
||||
setInterval(tickClock,1000);
|
||||
setInterval(render,30000);
|
||||
setInterval(renderDay,60000);
|
||||
|
||||
@@ -2,6 +2,7 @@ package ru.ua1zbe.bus127;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
@@ -10,6 +11,8 @@ public class MainActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// экран не гаснет, пока открыто расписание
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
WebView webView = new WebView(this);
|
||||
WebSettings settings = webView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
|
||||
136
web/index.html
136
web/index.html
@@ -16,8 +16,7 @@
|
||||
color:var(--txt);min-height:100vh;padding:24px 16px 48px}
|
||||
.wrap{max-width:760px;margin:0 auto}
|
||||
header{text-align:center;margin-bottom:18px}
|
||||
.route{font-size:12px;letter-spacing:.14em;color:var(--muted);text-transform:uppercase}
|
||||
h1{font-size:clamp(22px,4.5vw,30px);margin:6px 0 4px;font-weight:700}
|
||||
h1{font-size:clamp(22px,4.5vw,30px);margin:0 0 4px;font-weight:700}
|
||||
.sub{color:var(--muted);font-size:14px}
|
||||
.clock{font-size:22px;font-weight:600;margin-top:10px;color:var(--accent)}
|
||||
.clock small{color:var(--muted);font-weight:400;font-size:12px}
|
||||
@@ -35,6 +34,7 @@
|
||||
.where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px}
|
||||
|
||||
.dir{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
||||
.dir .card:only-child{grid-column:1 / -1} /* одна карточка — во всю ширину */
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
|
||||
.card h3{margin:0 0 4px;font-size:15px;font-weight:700}
|
||||
.card h3 .arr{color:var(--accent)}
|
||||
@@ -58,6 +58,15 @@
|
||||
|
||||
.tablecard{background:var(--card);border:1px solid var(--line);border-radius:14px;
|
||||
overflow:hidden;display:none;margin-bottom:8px}
|
||||
/* переключатель типа дня в полном расписании */
|
||||
.days{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;padding:12px 12px 10px;
|
||||
border-bottom:1px solid var(--line)}
|
||||
.days button{padding:9px 4px;border-radius:10px;border:1px solid var(--line);
|
||||
background:var(--card2);color:var(--muted);font-size:13px;font-weight:600;
|
||||
cursor:pointer;transition:.15s;font-family:inherit}
|
||||
.days button:hover{border-color:var(--accent2);color:var(--txt)}
|
||||
.days button.active{background:linear-gradient(135deg,var(--accent2),var(--accent));
|
||||
color:#04101c;border-color:transparent}
|
||||
.thead{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;background:var(--card2);
|
||||
font-size:11px;color:var(--muted);text-align:center}
|
||||
.thead div{padding:11px 6px;border-left:1px solid var(--line)}
|
||||
@@ -76,7 +85,6 @@
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<div class="route">Мурманская область · регулярный маршрут</div>
|
||||
<h1>Автобус №127</h1>
|
||||
<div class="sub">Кандалакша — Полярные Зори — Зашеек</div>
|
||||
<div class="clock" id="clock">--:--:-- <small>МСК</small></div>
|
||||
@@ -95,6 +103,11 @@
|
||||
|
||||
<button class="fullbtn" id="fullBtn">Полное расписание <span class="chev">▾</span></button>
|
||||
<section class="tablecard" id="fullTable">
|
||||
<div class="days" id="dayPick">
|
||||
<button data-day="weekday" class="active">Будни</button>
|
||||
<button data-day="saturday">Суббота</button>
|
||||
<button data-day="sunday">Воскресенье</button>
|
||||
</div>
|
||||
<div class="thead">
|
||||
<div><b>Кандалакша</b>отпр.</div>
|
||||
<div><b>Полярные Зори</b>→ Зашеек</div>
|
||||
@@ -108,7 +121,19 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const SCHEDULE = [
|
||||
// Расписание по типам дней недели.
|
||||
// Колонки: отправление из Кандалакши · Полярные Зори (в Зашеек) ·
|
||||
// прибытие в Зашеек · Полярные Зори (обратно).
|
||||
//
|
||||
// Будни — полная таблица маршрута. По субботе и воскресенью в репозитории
|
||||
// хранились только отправления из Полярных Зорь и прибытия в Зашеек
|
||||
// (списки в app/src/main/java/ru/ua1zbe/bus127/MainActivity.kt), поэтому
|
||||
// первая и четвёртая колонки восстановлены по тем же правилам, что и для
|
||||
// будней, и требуют сверки с официальным расписанием:
|
||||
// кол0 = кол1 − 50 мин (Кандалакша → Полярные Зори занимает ровно 50 мин)
|
||||
// кол3 = кол2 + 20 мин (так разность кол3−кол2 выглядит в 8 строках из 13)
|
||||
const SCHEDULES = {
|
||||
weekday: [
|
||||
["05:20","06:20","06:30","06:50"],
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["07:40","08:30","09:00","09:20"],
|
||||
@@ -122,19 +147,57 @@ const SCHEDULE = [
|
||||
["16:50","17:40","18:10","18:35"],
|
||||
["18:20","19:10","19:40","20:05"],
|
||||
["20:20","21:10","21:40","22:05"]
|
||||
];
|
||||
],
|
||||
saturday: [
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["07:30","08:20","08:45","09:05"],
|
||||
["08:45","09:35","10:05","10:25"],
|
||||
["10:10","11:00","11:30","11:50"],
|
||||
["12:30","13:20","13:50","14:10"],
|
||||
["14:00","14:50","15:45","16:05"],
|
||||
["15:30","16:20","16:45","17:05"],
|
||||
["17:00","17:50","18:20","18:40"],
|
||||
["18:10","19:00","19:30","19:50"],
|
||||
["20:20","21:10","21:40","22:00"]
|
||||
],
|
||||
sunday: [
|
||||
["06:10","07:00","07:25","07:45"],
|
||||
["08:45","09:35","10:05","10:25"],
|
||||
["12:30","13:20","13:50","14:10"],
|
||||
["14:00","14:50","15:25","15:45"],
|
||||
["16:50","17:40","18:10","18:30"],
|
||||
["20:20","21:10","21:40","22:00"]
|
||||
]
|
||||
};
|
||||
const DAY_KEYS = ["weekday","saturday","sunday"];
|
||||
const DAY_TITLES = {weekday:"Будни", saturday:"Суббота", sunday:"Воскресенье"};
|
||||
|
||||
const TRIPS_SHOWN = 5; // сколько ближайших рейсов показывать в карточке
|
||||
|
||||
// Время в пути Полярные Зори ↔ Кандалакша. В таблице расписания его нет:
|
||||
// в 12 строках из 13 Кандалакша → ПЗ занимает ровно 50 минут
|
||||
// (в первой строке 60 — по всей видимости опечатка).
|
||||
const RIDE_PZ_KAND = 50;
|
||||
|
||||
// arrs — список прибытий: {label, col} — время из колонки таблицы,
|
||||
// {label, offset} — «время в пути» от отправления (если col не задан).
|
||||
const DIRECTIONS = {
|
||||
// От Кандалакши и Зашейка все рейсы идут через Полярные Зори — одна карточка
|
||||
"Кандалакша": [
|
||||
{to:"Полярные Зори", dep:0, arr:1, via:"через 1 час"},
|
||||
{to:"Зашеек", dep:0, arr:2, via:"через Полярные Зори"}
|
||||
{to:"Полярные Зори, Зашеек", dep:0, via:"через Полярные Зори",
|
||||
arrs:[{label:"Полярные Зори", col:1},
|
||||
{label:"Зашеек", col:2}]}
|
||||
],
|
||||
"Полярные Зори": [
|
||||
{to:"Зашеек", dep:1, arr:2, via:"≈ 30 мин в пути"},
|
||||
{to:"Кандалакша", dep:3, arr:null, via:"≈ 1 час в пути"}
|
||||
{to:"Зашеек", dep:1, via:"≈ 30 мин в пути",
|
||||
arrs:[{label:"Зашеек", col:2}]},
|
||||
{to:"Кандалакша", dep:3, via:"≈ 50 мин в пути",
|
||||
arrs:[{label:"Кандалакша", offset:RIDE_PZ_KAND}]}
|
||||
],
|
||||
"Зашеек": [
|
||||
{to:"Полярные Зори", dep:2, arr:3, via:"≈ 20 мин в пути"},
|
||||
{to:"Кандалакша", dep:2, arr:null, via:"через Полярные Зори"}
|
||||
{to:"Полярные Зори, Кандалакша", dep:2, via:"через Полярные Зори",
|
||||
arrs:[{label:"Полярные Зори", col:3},
|
||||
{label:"Кандалакша", col:3, offset:RIDE_PZ_KAND}]}
|
||||
]
|
||||
};
|
||||
const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"];
|
||||
@@ -173,6 +236,12 @@ function nowMs(){
|
||||
second: d.getUTCSeconds(), day: d.getUTCDay() };
|
||||
}
|
||||
|
||||
function fmtHM(m){
|
||||
const p = n=>String(n).padStart(2,"0");
|
||||
m = ((m % 1440) + 1440) % 1440; // на случай перехода через полночь
|
||||
return `${p(Math.floor(m/60))}:${p(m%60)}`;
|
||||
}
|
||||
|
||||
function fmtWait(m){
|
||||
if(m <= 0) return "отправление сейчас";
|
||||
const h = Math.floor(m/60), mm = m%60;
|
||||
@@ -183,24 +252,38 @@ function fmtWait(m){
|
||||
|
||||
let currentStop = "Полярные Зори";
|
||||
|
||||
// Date#getDay(): 0 — воскресенье, 6 — суббота
|
||||
function dayKeyFor(dow){ return dow===0 ? "sunday" : dow===6 ? "saturday" : "weekday"; }
|
||||
|
||||
function sched(){ return SCHEDULES[activeDay]; }
|
||||
|
||||
function render(){
|
||||
const now = nowMs();
|
||||
const curMin = now.hour*60 + now.minute;
|
||||
const dirs = DIRECTIONS[currentStop];
|
||||
const data = sched();
|
||||
document.getElementById("dirBox").innerHTML = dirs.map(d=>{
|
||||
const upcoming = SCHEDULE
|
||||
.map((r,ri)=>({dep:toMin(r[d.dep]), arr:d.arr!==null?r[d.arr]:null, ri}))
|
||||
const upcoming = data
|
||||
.map((r,ri)=>{
|
||||
const dep = toMin(r[d.dep]);
|
||||
const arrs = d.arrs.map(a=>{
|
||||
const base = a.col!==undefined ? toMin(r[a.col]) : dep;
|
||||
return {label:a.label, t:fmtHM(base + (a.offset||0))};
|
||||
});
|
||||
return {dep, arrs, ri};
|
||||
})
|
||||
.filter(x=>x.dep >= curMin)
|
||||
.sort((a,b)=>a.dep-b.dep)
|
||||
.slice(0,3)
|
||||
.slice(0,TRIPS_SHOWN)
|
||||
.map(x=>{
|
||||
const w = x.dep - curMin;
|
||||
const arr = x.arr ? `<small>прибытие ${x.arr}</small>` : "";
|
||||
// .trip .t small имеет display:block, поэтому каждый <small> — своя строка
|
||||
const arr = x.arrs.map(a=>`<small>прибытие ${a.label} ${a.t}</small>`).join("");
|
||||
return `<div class="trip ${w<15?'soon':''}">
|
||||
<span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span>
|
||||
<span class="t">${data[x.ri][d.dep]}${arr}</span>
|
||||
<span class="w">${fmtWait(w)}</span>
|
||||
</div>`;
|
||||
}).join("") || `<div class="empty">на сегодня всё — первый завтра в ${SCHEDULE[0][d.dep]}</div>`;
|
||||
}).join("") || `<div class="empty">на сегодня всё — первый завтра в ${data[0][d.dep]}</div>`;
|
||||
return `<div class="card">
|
||||
<h3>→ <span class="arr">${d.to}</span></h3>
|
||||
<div class="via">${d.via}</div>
|
||||
@@ -210,7 +293,7 @@ function render(){
|
||||
}
|
||||
|
||||
function renderTable(){
|
||||
document.getElementById("tableBody").innerHTML = SCHEDULE.map(row =>
|
||||
document.getElementById("tableBody").innerHTML = sched().map(row =>
|
||||
`<div class="row"><div>${row[0]}</div><div>${row[1]}</div><div>${row[2]}</div><div>${row[3]}</div></div>`
|
||||
).join("");
|
||||
}
|
||||
@@ -220,6 +303,15 @@ function renderDay(){
|
||||
document.getElementById("dayBadge").innerHTML = `Сегодня <b>${DAYS[dow]}</b>`;
|
||||
}
|
||||
|
||||
// Подсветка выбранного типа дня и перерисовка расписания
|
||||
function selectDay(key){
|
||||
activeDay = key;
|
||||
document.querySelectorAll("#dayPick button").forEach(b=>
|
||||
b.classList.toggle("active", b.dataset.day === key));
|
||||
render();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function tickClock(){
|
||||
const d = nowMs();
|
||||
const p = n=>String(n).padStart(2,"0");
|
||||
@@ -238,6 +330,10 @@ document.querySelectorAll(".pick").forEach(btn=>{
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("#dayPick button").forEach(btn=>{
|
||||
btn.addEventListener("click",()=>selectDay(btn.dataset.day));
|
||||
});
|
||||
|
||||
document.getElementById("fullBtn").addEventListener("click",()=>{
|
||||
const t = document.getElementById("fullTable");
|
||||
const b = document.getElementById("fullBtn");
|
||||
@@ -246,7 +342,9 @@ document.getElementById("fullBtn").addEventListener("click",()=>{
|
||||
b.classList.toggle("open", !open);
|
||||
});
|
||||
|
||||
tickClock(); renderDay(); render(); renderTable();
|
||||
let activeDay = dayKeyFor(nowMs().day); // по умолчанию — реальный день недели
|
||||
selectDay(activeDay);
|
||||
tickClock(); renderDay();
|
||||
setInterval(tickClock,1000);
|
||||
setInterval(render,30000);
|
||||
setInterval(renderDay,60000);
|
||||
|
||||
Reference in New Issue
Block a user