@@ -65,7 +65,7 @@
color : var ( - - txt ) ; border-radius : 10 px ; width : 38 px ; height : 38 px ;
font-size : 22 px ; line-height : 1 ; font-family : inherit ; cursor : pointer ;
align-items : center ; justify-content : center ; padding : 0 }
body . app . iconbtn , body . about . iconbtn { display : inline-flex }
body . app . iconbtn , body . about . iconbtn , body . map . iconbtn { display : inline-flex }
/* на главном экране стрелки нет, но место под неё оставляем, иначе съедет центр */
body . app . iconbtn . back { visibility : hidden }
/* троеточие в круглой кнопке приподнято: рамка остаётся на одной линии с названием */
@@ -95,9 +95,12 @@
. aboutcard p . muted { color : var ( - - muted ) ; font-size : 13 px }
/* экран «Карта»: страница целиком занята картой, шапка остаётся */
body . map { padding : 24 px 16 px 16 px ; overflow : hidden }
body . map . wrap { height : 100 vh ; height : 100 dvh ; display : flex ; flex-direction : column ; max-width : none }
body . map { height : 100 vh ; height : 100 dvh ; min-height : 0 ; padding: 24 px 16 px 16 px ; overflow : hidden }
body . map . wrap { height : 100 % ; display : flex ; flex-direction : column ; max-width : none }
body . map header { margin-bottom : 12 px }
/* заголовок всегда в одну строку, в том числе на карте */
body . map h1 { font-size : 20 px ; line-height : 1.2 ; white-space : nowrap }
body . map . clock { font-size : 18 px ; margin-top : 6 px }
body . map . day , body . map . pickers , body . map . where , body . map . dir ,
body . map . fullbtn , body . map # fullTable , body . map . aboutcard , body . map footer { display : none }
body . map # mapBox { flex : 1 1 auto ; min-height : 0 ; display : flex ; flex-direction : column ; gap : 8 px }
@@ -106,8 +109,23 @@
. mapcanvas . loading :: after { content : "Загружаю карту…" ; position : absolute ; inset : 0 ;
display : flex ; align-items : center ; justify-content : center ;
color : var ( - - muted ) ; font-size : 14 px }
. mapstatus { flex : 0 0 auto ; background : var ( - - card ) ; border : 1 px solid var ( - - line ) ;
border-radius : 14 px ; padding : 11 px 14 px }
/* нижняя шторка: свёрнута до ручки с заголовком, тянется вверх,
а текст внутри прокручивается отдельно от карты */
. mapsheet { flex : 0 0 auto ; position : relative ; display : flex ; flex-direction : column ;
background : var ( - - card ) ; border : 1 px solid var ( - - line ) ; border-radius : 14 px ;
overflow : hidden ; box-shadow : 0 -6 px 20 px rgba ( 0 , 0 , 0 , .35 ) }
. grab { position : relative ; flex : 0 0 auto ; display : flex ; align-items : center ; gap : 8 px ;
padding : 14 px 14 px 8 px ; cursor : grab ; touch-action : none ; user-select : none ;
border-bottom : 1 px solid var ( - - line ) }
. grab . bar { position : absolute ; top : 6 px ; left : 50 % ; width : 34 px ; height : 4 px ; margin-left : -17 px ;
border-radius : 2 px ; background : #3a4a70 }
. grab . cap { flex : 1 1 auto ; min-width : 0 ; font-size : 12 px ; font-weight : 700 ; color : var ( - - muted ) ;
overflow : hidden ; text-overflow : ellipsis ; white-space : nowrap }
. grab . chev { color : var ( - - muted ) ; font-size : 11 px ; transition : transform .2 s }
. mapsheet . open . grab . chev { transform : rotate ( 180 deg ) }
. mapstatus { flex : 1 1 auto ; min-height : 0 ; overflow-y : auto ; -webkit- overflow-scrolling : touch ;
overscroll-behavior : contain ; padding : 11 px 14 px 14 px }
. mapstatus . head { font-size : 14 px ; font-weight : 700 ; margin-bottom : 4 px }
. mapstatus . head b { color : var ( - - accent ) }
. mapstatus . rows { font-size : 13 px ; color : var ( - - muted ) ; line-height : 1.55 }
@@ -115,6 +133,11 @@
. mapstatus . rows . when { color : var ( - - txt ) ; font-variant-numeric : tabular-nums ;
white-space : nowrap ; font-size : 12 px }
. mapstatus . hint { margin-top : 7 px ; font-size : 11 px ; color : var ( - - muted ) ; line-height : 1.4 }
. note { margin-top : 10 px ; padding-top : 10 px ; border-top : 1 px solid var ( - - line ) ;
font-size : 12 px ; line-height : 1.55 ; color : var ( - - muted ) }
. note p { margin : 0 0 8 px }
. note p : last-child { margin-bottom : 0 }
. note b { color : var ( - - txt ) }
/* метки на карте. Корень двигает библиотека, вид задаёт внутренний блок */
. pin { transform : translate ( -50 % , -100 % ) ; white-space : nowrap ; pointer-events : none }
@@ -197,20 +220,44 @@
< / div >
< div class = "thead" >
< div > < b > Кандалакша< / b > отпр.< / div >
< div > < b > Полярные Зори< / b > → Зашеек< / div >
< div > < b > Зашеек< / b > прибытие < / div >
< div > < b > Полярные Зори< / b > → Кандалакша < / div >
< div > < b > Полярные Зори< / b > отпр. в Зашеек< / div >
< div > < b > Зашеек< / b > отпр. < / div >
< div > < b > Полярные Зори< / b > отпр. в Канд. < / div >
< / div >
< div id = "tableBody" > < / div >
< / section >
< section class = "mapcard" id = "mapBox" hidden >
< div class = "mapcanvas loading" id = "mapCanvas" > < / div >
< div class = "mapstatus " >
< div class = "head " id = "mapHead" > Карта маршрута< / div >
< div class = "rows" id = "mapRows " > < / div >
< div class = "hint" > Положение автобуса рассчитано по расписанию, а не по GPS:
между остановками интерполируется по дороге. Точность — до нескольких минут. < / div >
< div class = "mapsheet" id = "mapSheet " >
< div class = "grab " id = "sheetGrab" >
< span class = "bar " > < / span >
< span class = "cap" id = "sheetCap" > Автобусы и расписание< / span >
< span class = "chev" > ▴< / span >
< / div >
< div class = "mapstatus" >
< div class = "head" id = "mapHead" > Карта маршрута< / div >
< div class = "rows" id = "mapRows" > < / div >
< div class = "hint" > Положение автобуса рассчитано по расписанию, а не по GPS:
между остановками интерполируется по дороге. Точность — до нескольких минут.< / div >
< div class = "note" >
< p > < b > Как читать расписание.< / b > Таблица и списки показывают официальное время
< b > отправлений< / b > . Время прибытия в расписании не публикуется: оно берётся
из отправления следующей остановки, потому что автобус приезжает
на Полярные Зори и сразу уходит дальше. Исключение — Зашеек: там
прибытие посчитано как выезд из Полярных Зорь плюс 20 минут.
Получившиеся интервалы в о б е стороны по одной дороге отличаются
на 5– 10 минут — так и есть в официальном расписании, это не ошибка.< / p >
< p > < b > Положение автобусов.< / b > GPS у приложения нет, координаты
интерполируются по дороге между остановками по времени рейса.
В Зашейке автобус стоит, пока не подошло время обратного выезда.
Точность — до нескольких минут, в пробки отставание больше.< / p >
< p class = "muted" > Расписание сверено с официальным: отправления взяты из данных
диспетчерской службы перевозчика, летнее расписание
с 01.05.2026 по 31.10.2026, время московское. Карта и тайлы — VK Карты,
геометрия дороги — OpenStreetMap.< / p >
< / div >
< / div >
< / div >
< / section >
@@ -224,36 +271,47 @@
< p > В меню есть «Карта»: там видно, где сейчас едут автобусы, и как пройден
участок между остановками. Положение считается по расписанию — приложение
не получает данные с GPS, поэтому это оценка с точностью до нескольких минут.< / p >
< p class = "muted" > Расписание действует с 30.09.2020. Время московское (М С К ).< / p >
< p class = "muted" > Расписание сверено с официальным: отправления взяты из данных
диспетчерской службы перевозчика, летнее расписание
с 01.05.2026 по 31.10.2026. Время московское (М С К ).< / p >
< p class = "muted" > Карта и тайлы — VK Карты, геометрия дороги — OpenStreetMap.
Пуш-уведомления приходят через RuStore Push — для этого нужен
установленный и авторизованный RuStore на устройстве.< / p >
< / section >
< footer > Р асписание действует с 30.09 .2020 · время московское (М С К )< / footer >
< footer > Летнее р асписание с 01.05.2026 по 31.10 .2026 · отправления по данным
диспетчерской службы перевозчика · время московское (М С К )< / footer >
< / div >
< script >
// Расписание по типам дней недели.
// Колонки: отправление из Кандалакши · Полярные Зори (в Зашеек) ·
// прибытие в Зашеек · Полярные Зори (обратно).
// Расписание по типам дней недели. Взято с официального расписания
// (данные диспетчерской службы перевозчика О О О «Гарант-Авто»,
// kandalaksha.org/static/auto.html), летнее расписание
// с 01.05.2026 по 31.10.2026.
//
// Будни — полная таблица маршрута. По субботе и воскресенью в репозитории
// хранились только отправления из Полярных Зорь и прибытия в Зашеек
// (списки в app/src/main/java/ru/ua1zbe/bus127/MainActivity.kt), поэтому
// первая и четвёртая колонки восстановлены по тем же правилам, что и для
// будней, и требуют сверки с официальным расписанием:
// кол0 = ко л1 − 50 мин (Кандалакша → Полярные Зори занимает ровно 50 мин )
// ко л3 = ко л2 + 20 мин (так разность ко л3− ко л2 выглядит в 8 строках из 13)
// Опубликованы только ОТПРАВЛЕНИЯ, и четыре колонки склеиваются
// в один круг рейса попарно, 1:1 — проверено, что моменты строго
// возрастают в каждой строке:
// ко л0 — отправление из Кандалакши
// ко л1 — отправление из Полярных Зорь в Зашеек
// кол2 — отправление ИЗ Зашейка (обратно в Полярные Зори )
// ко л3 — отправление из Полярных Зорь в Кандалакшу
//
// Важно: ко л2 — это выезд из Зашейка, а не прибытие в него.
// Раньше колонка была подписана как прибытие, и из-за этого один и тот же
// автобус «прибывал» и «уезжал» в Зашеек в одну минуту, а интервалы
// туда и обратно выходили разными на одной и той же дороге.
// Прибытие в Зашеек в расписании не публикуется — оно считается
// отдельно, см. arrZasheek ниже.
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" ] ,
[ "08:00" , "08:50" , "09:20" , "09:50" ] ,
[ "09:45" , "10:35" , "11:05" , "11:25" ] ,
[ "11:00" , "11:50" , "12:20" , "12:40" ] ,
[ "12 :30" , "13 :20" , "13 :50" , "14:1 0" ] ,
[ "11 :30" , "12 :20" , "12 :50" , "13:2 0" ] ,
[ "12:30" , "13:20" , "13:50" , "14:15" ] ,
[ "13:30" , "14:20" , "14:50" , "15:10" ] ,
[ "14:00" , "14:50" , "15:25" , "15:50" ] ,
[ "15:30" , "16:20" , "17:00" , "17:25" ] ,
@@ -263,23 +321,23 @@ const SCHEDULES = {
] ,
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 " ]
[ "07:30" , "08:20" , "08:45" , "09:1 0" ] ,
[ "08:45" , "09:35" , "10:05" , "10:30 " ] ,
[ "10:10" , "11:00" , "11:30" , "11:55 " ] ,
[ "12:30" , "13:20" , "13:50" , "14:15 " ] ,
[ "14:00" , "14:50" , "15:45" , "16:1 0" ] ,
[ "15:30" , "16:20" , "16:45" , "17:1 0" ] ,
[ "17:00" , "17:50" , "18:20" , "18:45 " ] ,
[ "18:10" , "19:00" , "19:30" , "19:55 " ] ,
[ "20:20" , "21:10" , "21:40" , "22:05 " ]
] ,
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:4 5" ] ,
[ "16:50" , "17:40" , "18:10" , "18:30 " ] ,
[ "20:20" , "21:10" , "21:40" , "22:00 " ]
[ "08:45" , "09:35" , "10:05" , "10:30 " ] ,
[ "12:30" , "13:20" , "13:50" , "14:15 " ] ,
[ "14:00" , "14:50" , "15:25" , "15:50 " ] ,
[ "16:50" , "17:40" , "18:10" , "18:35 " ] ,
[ "20:20" , "21:10" , "21:40" , "22:05 " ]
]
} ;
const DAY _KEYS = [ "weekday" , "saturday" , "sunday" ] ;
@@ -289,21 +347,38 @@ const TRIPS_SHOWN = 5; // сколько ближайших рейсов по
// Время в пути Полярные Зори ↔ Кандалакша. В таблице расписания е г о нет:
// в 12 строках из 13 Кандалакша → ПЗ занимает ровно 50 минут
// (в первой строке 60 — по всей видимости опечатка).
// (в первой строке 60 — по всей видимости опечатка в самом расписании ).
const RIDE _PZ _KAND = 50 ;
// arrs — список прибытий: {label, col} — время из колонки таблицы,
// {label, offset} — «время в пути» от отправления (если col не задан).
// Полярные Зори → Зашеек. В расписании тоже нет: там опубликованы только
// отправления. Принимаем 20 минут — но не больше, чем есть между выездом
// из Полярных Зорь и выездом обратно, иначе на первом утреннем рейсе
// (06:20 → 06:30) автобус «прибыл» бы в Зашеек уже после своего выезда.
const RIDE _PZ _ZASHEEK = 20 ;
// Прибытие в Зашеек — оценка, потому что официального времени прибытия
// в расписании нет. Считаем от выезда из Полярных Зорь.
function arrZasheek ( row ) {
const t1 = toMin ( row [ 1 ] ) , t2 = toMin ( row [ 2 ] ) ;
return t1 + Math . max ( 0 , Math . min ( RIDE _PZ _ZASHEEK , t2 - t1 ) ) ;
}
// arrs — список прибытий: {label, col} — время из колонки расписания,
// {label, offset} — «время в пути» от отправления (если col не задан),
// {label, arrZ:true} — оценка прибытия в Зашеек, см. arrZasheek().
// В расписании опубликованы только отправления, поэтому прибытия
// (кроме Зашейка, где есть своя оценка) берутся из отправлений следующей
// остановки: автобус приезжает на ПЗ и сразу уходит дальше.
const DIRECTIONS = {
// От Кандалакши и Зашейка все рейсы идут через Полярные Зори — одна карточка
"Кандалакша" : [
{ to : "Полярные Зори, Зашеек" , dep : 0 , via : "через Полярные Зори" ,
arrs : [ { label : "Полярные Зори" , col : 1 } ,
{ label : "Зашеек" , col : 2 } ] }
{ label : "Зашеек" , arrZ : true } ] }
] ,
"Полярные Зори" : [
{ to : "Зашеек" , dep : 1 , via : "≈ 3 0 мин в пути" ,
arrs : [ { label : "Зашеек" , col : 2 } ] } ,
{ to : "Зашеек" , dep : 1 , via : "≈ 2 0 мин в пути" ,
arrs : [ { label : "Зашеек" , arrZ : true } ] } ,
{ to : "Кандалакша" , dep : 3 , via : "≈ 50 мин в пути" ,
arrs : [ { label : "Кандалакша" , offset : RIDE _PZ _KAND } ] }
] ,
@@ -384,7 +459,9 @@ function render(){
. 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 ;
const base = a . col !== undefined ? toMin ( r [ a . col ] )
: a . arrZ ? arrZasheek ( r )
: dep ;
return { label : a . label , t : fmtHM ( base + ( a . offset || 0 ) ) } ;
} ) ;
return { dep , arrs , ri } ;
@@ -523,6 +600,7 @@ let KAND_TO_PZ = null; // Кандалакша → Полярные Зори
let PZ _TO _ZASHEEK = null ; // Полярные Зори → Зашеек
let PZ _TO _ZASHEEK _BACK = null ;
let KAND _TO _PZ _BACK = null ;
let MAPREF = null ; // карта нужна шторке, чтобы пересчитать размер холста
// Длина ломаной с накопленными отрезками. Расстояние считаем в
// равнопромежуточной проекции — на таком масштабе это точнее градусов.
@@ -553,21 +631,25 @@ function pointAt(p, f){
return [ x1 + ( x2 - x1 ) * t , y1 + ( y2 - y1 ) * t ] ;
}
// Рейс разложен на четыре участка: туда Кандалакша → ПЗ → Зашеек и обратно .
// Время в пути ПЗ → Кандалакша в таблице не указано, берём те же 50 минут,
// что и в DIRECTIONS.
// Рейс разложен на участки круга: Кандалакша → ПЗ → Зашеек → ПЗ → Кандалакша .
// Колонка ко л2 — это выезд ИЗ Зашейка, поэтому между прибытием туда
// (оценка arrZasheek) и выездом автобус стоит на остановке: отдельный
// участок hold, на котором точка не двигается. Время в пути ПЗ → Кандалакша
// в расписании не указано, берём те же 50 минут, что и в DIRECTIONS.
function makeRun ( row ) {
const t0 = toMin ( row [ 0 ] ) , t1 = toMin ( row [ 1 ] ) , t2 = toMin ( row [ 2 ] ) , t3 = toMin ( row [ 3 ] ) ;
const back = t3 + RIDE _PZ _KAND ; // прибытие в Кандалакшу
return {
t0 , tEnd : back ,
segs : [
{ a : t0 , b : t1 , line : KAND _TO _PZ , to : "Полярные Зори" , end : t1 } ,
{ a : t1 , b : t2 , line : PZ _TO _ZASHEEK , to : "Зашеек" , end : t2 } ,
{ a : t2 , b : t3 , line : PZ _TO _ZASHEEK _BACK , to : "Полярные Зори" , end : t3 } ,
{ a : t3 , b : back , line : KAND _TO _PZ _BACK , to : "Кандалакша" , end : back }
]
} ;
const z = arrZasheek ( row ) ; // прибытие в Зашеек (оценка)
const back = t3 + RIDE _PZ _KAND ; // прибытие в Кандалакшу
const segs = [
{ a : t0 , b : t1 , line : KAND _TO _PZ , to : "Полярные Зори" , end : t1 } ,
{ a : t1 , b : z , line : PZ _TO _ZASHEEK , to : "Зашеек" , end : z }
] ;
if ( t2 > z ) segs . push ( { a : z , b : t2 , line : PZ _TO _ZASHEEK , to : "Зашеек" , end : t2 , hold : true } ) ;
segs . push (
{ a : t2 , b : t3 , line : PZ _TO _ZASHEEK _BACK , to : "Полярные Зори" , end : t3 } ,
{ a : t3 , b : back , line : KAND _TO _PZ _BACK , to : "Кандалакша" , end : back }
) ;
return { t0 , tEnd : back , segs : segs . filter ( s => s . b > s . a ) } ;
}
// В с е рейсы, которые сейчас в пути, вместе с точкой и участком
@@ -580,7 +662,8 @@ function busesNow(){
if ( T < run . t0 || T > run . tEnd ) return ;
const seg = run . segs . find ( s => T >= s . a && T <= s . b ) ;
if ( ! seg ) return ;
const f = seg . b > seg . a ? ( T - seg . a ) / ( seg . b - seg . a ) : 0 ;
// на участке стоянки в Зашейке точка не двигается — держим конец подъезда
const f = seg . hold ? 1 : ( seg . b > seg . a ? ( T - seg . a ) / ( seg . b - seg . a ) : 0 ) ;
out . push ( { idx , seg , at : pointAt ( seg . line , f ) } ) ;
} ) ;
return out ;
@@ -606,8 +689,9 @@ function renderMapStatus(list){
}
const many = list . length > 1 ;
head . innerHTML = ` Сейчас в пути <b> ${ list . length } </b> ${ many ? "автобуса" : "автобус" } ` ;
rows . innerHTML = list . map ( b =>
` <div><span class="when">→ ${ b . seg . to } </span><span>прибытие в ${ fmtHM ( b . seg . end ) } </span></div> `
rows . innerHTML = list . map ( b => b . seg . hold
? ` <div><span class="when">Зашеек</span><span>стоит на остановке, выезд в ${ fmtHM ( b . seg . b ) } </span></div> `
: ` <div><span class="when">→ ${ b . seg . to } </span><span>прибытие в ${ fmtHM ( b . seg . end ) } </span></div> `
) . join ( "" ) ;
}
@@ -703,6 +787,7 @@ function initMap(){
zoom : 10 ,
attributionControl : true
} ) ;
MAPREF = map ;
map . on ( "load" , ( ) => {
ready = true ;
@@ -748,7 +833,60 @@ function initMap(){
} ) ;
}
if ( MODE === "map" ) initMap ( ) ;
// Нижняя шторка карты. З а ручку её тянут вверх, короткое касание — переключает.
// Внутри своя прокрутка, поэтому карта под текстом не ездит, а карта
// VK перерисовывается под изменившуюся высоту холста.
function initSheet ( ) {
const box = document . getElementById ( "mapBox" ) ;
const sheet = document . getElementById ( "mapSheet" ) ;
const grab = document . getElementById ( "sheetGrab" ) ;
if ( ! box || ! sheet || ! grab ) return ;
const MIN = 118 ;
const max = ( ) => Math . max ( MIN , Math . round ( box . clientHeight * 0.72 ) ) ;
let open = false , h = MIN , startY = 0 , startH = 0 , moved = 0 , dragged = false ;
function set ( next , expanded ) {
h = Math . max ( MIN , Math . min ( max ( ) , Math . round ( next ) ) ) ;
sheet . style . height = h + "px" ;
open = ! ! expanded ;
sheet . classList . toggle ( "open" , open ) ;
if ( MAPREF ) MAPREF . resize ( ) ; // холст карты стал ниже — пересчитать
}
// Захват указателя есть не во всех окружениях (и может не сработать при
// синтетическом событии), поэтому на него не опираемся критично.
const captured = e => { try { return grab . hasPointerCapture ( e . pointerId ) ; }
catch ( _ ) { return true ; } } ;
const capture = e => { try { grab . setPointerCapture ( e . pointerId ) ; } catch ( _ ) { } } ;
const release = e => { try { grab . releasePointerCapture ( e . pointerId ) ; } catch ( _ ) { } } ;
grab . addEventListener ( "pointerdown" , e => {
startY = e . clientY ; startH = h ; moved = 0 ; dragged = false ;
capture ( e ) ;
} ) ;
grab . addEventListener ( "pointermove" , e => {
if ( ! captured ( e ) ) return ;
moved = startY - e . clientY ;
if ( Math . abs ( moved ) > 6 ) dragged = true ;
const want = startH + moved ;
set ( want , want > MIN + 8 ) ;
} ) ;
grab . addEventListener ( "pointerup" , e => {
release ( e ) ;
if ( ! dragged ) { set ( open ? MIN : max ( ) , ! open ) ; return ; } // просто тап
const expanded = h > box . clientHeight * 0.4 ;
set ( expanded ? max ( ) : MIN , expanded ) ;
} ) ;
grab . addEventListener ( "pointercancel" , ( ) => { set ( open ? max ( ) : MIN , open ) ; } ) ;
// поворот экрана или смена высоты окна — пересчитать предел раскрытия
window . addEventListener ( "resize" , ( ) => set ( open ? max ( ) : MIN , open ) ) ;
set ( MIN , false ) ;
}
if ( MODE === "map" ) { initMap ( ) ; initSheet ( ) ; }
< / script >
< / body >