(function (){
'use strict';
const PROVIDER_LABELS={
a: 'Альфа',
p: 'Плюс',
u: 'Юни',
e: 'Альфа',
z: 'Плюс',
k: 'Юни'
};
const PROVIDER_DESCRIPTIONS={
a: 'Альфа — идеален для поездки в одну-две страны. Выбираете направление, покупаете пакет и пользуетесь интернетом без лишних настроек.',
e: 'Альфа — идеален для поездки в одну-две страны. Выбираете направление, покупаете пакет и пользуетесь интернетом без лишних настроек.',
u: 'Юни — создан для тех, кто посещает несколько стран за одну поездку. Одна eSIM, несколько пакетов для разных стран: карточка сама определит, в какой стране вы находитесь, и автоматически активирует нужный пакет.',
k: 'Юни — создан для тех, кто посещает несколько стран за одну поездку. Одна eSIM, несколько пакетов для разных стран: карточка сама определит, в какой стране вы находитесь, и автоматически активирует нужный пакет.',
p: 'Плюс — аналог Простого тарифа, но с важным преимуществом для пользователей Android: в ряде стран не потребуются дополнительные настройки устройства. Подключение — в несколько касаний.',
z: 'Плюс — аналог Простого тарифа, но с важным преимуществом для пользователей Android: в ряде стран не потребуются дополнительные настройки устройства. Подключение — в несколько касаний.'
};
window.REDESIM_PROVIDER_LABELS=PROVIDER_LABELS;
window.REDESIM_PROVIDER_DESCRIPTIONS=PROVIDER_DESCRIPTIONS;
let selDays=null;
let selGb=null;
const selMap={};
const userPicked=new Set();
const selectedOperatorKeys=new Set();
let operatorFilterItemsCache=null;
let operatorPickerScrollY=0;
let operatorPickerBodyLocked=false;
let operatorPickerCleanup=null;
const fmt=n => n.toLocaleString('ru-RU') + ' ₽';
function normalizeOperatorFilterKey(value){
return String(value||'')
.trim()
.toLowerCase()
.replace(/\s+/g, ' ');
}
function getRowOperatorItems(row){
const raw=String(row?.dataset?.networkList||'').trim();
if(!raw) return [];
const seen=new Set();
const items=[];
raw.split(',').forEach(part=> {
const name=part.trim();
const key=normalizeOperatorFilterKey(name);
if(!name||!key||seen.has(key)) return;
seen.add(key);
items.push({ key, name });
});
return items;
}
function rowMatchesSelectedOperators(row){
if(!selectedOperatorKeys.size) return true;
const rowKeys=getRowOperatorItems(row).map(item=> item.key);
return rowKeys.some(key=> selectedOperatorKeys.has(key));
}
function escapeInlineTariffHtml(str){
return String(str||'')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
const _plural=new Intl.PluralRules('ru');
function dW(n){
return { one: 'день', few: 'дня', many: 'дней', other: 'дней' }[_plural.select(n)] ?? 'дней';
}
function adjustDays(delta){
const inp=document.getElementById('dayInp');
if(!inp) return;
const current=parseInt(inp.value, 10)||7;
let v=Math.min(180, Math.max(1, current + delta));
inp.value=v;
inp.dispatchEvent(new Event('input'));
}
const DAILY_VOLUME_MARKER=999999999999;
function isDaily(row){
return row.dataset.type==='daily'||parseInt(row.dataset.volume, 10) >=DAILY_VOLUME_MARKER;
}
function findDaysFilterTarget(selDaysValue){
const periods=new Set();
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
if(isDaily(row)) return;
if(!rowMatchesSelectedOperators(row)) return;
if(selGb==='unlimited') return;
if(selGb!==null){
const gb=parseInt(row.dataset.volume, 10)||0;
if(gb!==selGb) return;
}
const period=parseInt(row.dataset.period, 10);
if(period) periods.add(period);
});
if(periods.has(selDaysValue)) return { target: selDaysValue, isFallback: false };
const greater=Array.from(periods).filter(p=> p > selDaysValue).sort((a, b)=> a - b);
if(greater.length) return { target: greater[0], isFallback: true };
return { target: null, isFallback: false };}
function isSuitable(row, dayFilterInfo){
if(!rowMatchesSelectedOperators(row)) return false;
if(window.RedesimLocationFilter &&
typeof window.RedesimLocationFilter.matchesRow==='function' &&
!window.RedesimLocationFilter.matchesRow(row)){
return false;
}
const daily=isDaily(row);
if(selGb==='unlimited'){
if(!daily) return false;
}else if(selGb!==null){
if(daily) return false;
const gb=parseInt(row.dataset.volume, 10)||0;
if(gb!==selGb) return false;
}
if(daily){
return true;
}
if(dayFilterInfo){
if(dayFilterInfo.target===null) return false;
const period=parseInt(row.dataset.period, 10);
if(period!==dayFilterInfo.target) return false;
}
return true;
}
let _filterTimer=null;
function scheduleFilter(){
clearTimeout(_filterTimer);
_filterTimer=setTimeout(()=> {
filterTariffs();
updateFilterBadge();
}, 60);
}
function getTariffSortPrice(row){
if(!row) return Number.MAX_SAFE_INTEGER;
const price=parseInt(row.dataset.price, 10)||0;
if(isDaily(row)&&row.dataset.prov!=='z'){
return price * (selDays!==null ? selDays:7);
}
return price;
}
function sortTariffCardsByPrice(rowsDiv){
if(!rowsDiv) return;
const oldDivider=rowsDiv.querySelector('.tariff-group-divider');
if(oldDivider) oldDivider.remove();
const cards=Array.from(rowsDiv.children).filter(el=> !el.classList.contains('tariff-group-divider')
);
const cmp=(a, b)=> {
const ra=a.querySelector('.t-row');
const rb=b.querySelector('.t-row');
if(!ra||!rb) return 0;
const priceA=getTariffSortPrice(ra);
const priceB=getTariffSortPrice(rb);
if(priceA!==priceB) return priceA - priceB;
const volA=parseInt(ra.dataset.volume, 10)||0;
const volB=parseInt(rb.dataset.volume, 10)||0;
if(volA!==volB) return volA - volB;
const perA=parseInt(ra.dataset.period, 10)||0;
const perB=parseInt(rb.dataset.period, 10)||0;
return perA - perB;
};
const limited=cards.filter(card=> {
const row=card.querySelector('.t-row');
return row&&!isDaily(row);
});
const unlimited=cards.filter(card=> {
const row=card.querySelector('.t-row');
return row&&isDaily(row);
});
limited.sort(cmp);
unlimited.sort(cmp);
limited.forEach(card=> rowsDiv.appendChild(card));
const hasVisibleLimited=limited.some(c=> c.style.display!=='none');
const hasVisibleUnlimited=unlimited.some(c=> c.style.display!=='none');
if(limited.length&&unlimited.length){
const divider=document.createElement('div');
divider.className='tariff-group-divider';
divider.innerHTML='<span>Безлимитные пакеты</span>';
divider.style.display=(hasVisibleLimited&&hasVisibleUnlimited) ? '':'none';
rowsDiv.appendChild(divider);
}
unlimited.forEach(card=> rowsDiv.appendChild(card));
}
function filterTariffs(){
const days=selDays!==null ? selDays:7;
const dayFilterInfo=selDays!==null ? findDaysFilterTarget(selDays):null;
const rCount=document.getElementById('rCount');
let visible=0;
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
const card=row.closest('.tariff-card')||row.parentElement;
const show=isSuitable(row, dayFilterInfo);
if(card){
card.style.display=show ? '':'none';
card.classList.toggle('is-hidden', !show);
}
if(show) visible++;
if(!isDaily(row)){
const badgesEl=row.querySelector('.t-badges');
const existingFallback=badgesEl?.querySelector('.tb-fallback');
if(existingFallback) existingFallback.remove();
if(show&&dayFilterInfo&&dayFilterInfo.isFallback &&
parseInt(row.dataset.period, 10)===dayFilterInfo.target){
if(badgesEl){
const diff=dayFilterInfo.target - selDays;
const badge=document.createElement('span');
badge.className='t-badge tb-fallback';
badge.textContent=`+${diff} ${dW(diff)} в подарок`;
badgesEl.prepend(badge);
}}
}
if(isDaily(row)){
const pricePerDay=parseInt(row.dataset.price, 10);
const total=row.dataset.prov==='z' ? pricePerDay:pricePerDay * days;
const priceEl=row.querySelector('.t-price');
const ppgEl=row.querySelector('.t-ppg');
if(priceEl) priceEl.textContent=fmt(total);
if(ppgEl){
if(row.dataset.prov==='z'){
const rowDays=parseInt(row.dataset.period, 10)||1;
ppgEl.textContent=fmt(Math.ceil(pricePerDay / (2 * rowDays))) + '/ГБ';
}else{
ppgEl.textContent=row.dataset.gpd||fmt(pricePerDay) + '/день';
}}
const nameEl=row.querySelector('.t-name-main');
if(nameEl){
if(row.dataset.prov==='z'){
const gpd=parseFloat(row.dataset.gpd)||2;
const rowDays=parseInt(row.dataset.period, 10);
let multEl=nameEl.querySelector('.t-daily-mult');
if(!multEl){
multEl=document.createElement('span');
multEl.className='t-daily-mult';
}
multEl.textContent=` на ${rowDays} ${dW(rowDays)}`;
nameEl.textContent=`${gpd} ГБ в день`;
nameEl.appendChild(multEl);
}else{
let multEl=nameEl.querySelector('.t-daily-mult');
if(!multEl){
multEl=document.createElement('span');
multEl.className='t-daily-mult';
nameEl.appendChild(multEl);
}
multEl.textContent=` на ${days} ${dW(days)}`;
}}
}else{
const ppgEl=row.querySelector('.t-ppg');
if(ppgEl){
const price=parseInt(row.dataset.price, 10);
const vol=parseInt(row.dataset.volume, 10);
if(price > 0&&vol > 0){
ppgEl.textContent=fmt(Math.ceil(price / vol)) + '/ГБ';
}}
}});
if(rCount) rCount.textContent=visible + ' вариантов';
const emptyEl=document.getElementById('emptyState');
if(!document.querySelector('.empty--visible')){
if(emptyEl) emptyEl.classList.toggle('show', visible===0);
}
document.querySelectorAll('.prov-col').forEach(col=> {
const provKey=col.id.replace('col-', '');
const rowsDiv=col.querySelector('.tariff-rows');
const visRows=[...col.querySelectorAll('.t-row')]
.filter(r=> {
const card=r.closest('.tariff-card')||r.parentElement;
return card&&card.style.display!=='none'&&!card.classList.contains('is-hidden');
});
col.querySelectorAll('.t-badges .tb-val').forEach(b=> b.remove());
const allFixedRows=[...col.querySelectorAll('.t-row')].filter(r=> !isDaily(r));
if(allFixedRows.length > 1){
const cheapestPpg=allFixedRows.reduce((a, b)=> {
const ppgA=parseInt(a.dataset.price, 10) / parseInt(a.dataset.volume, 10);
const ppgB=parseInt(b.dataset.price, 10) / parseInt(b.dataset.volume, 10);
return ppgA <=ppgB ? a:b;
});
const isCheapestVisible=visRows.includes(cheapestPpg);
if(isCheapestVisible){
const el=cheapestPpg.querySelector('.t-badges');
if(el){
const badge=document.createElement('span');
badge.className='t-badge tb-val';
badge.textContent='Выгодно';
el.prepend(badge);
}}
}
if(rowsDiv){
sortTariffCardsByPrice(rowsDiv);
}
col.querySelectorAll('.t-row').forEach(r=> {
const sel=userPicked.has(provKey)&&r.dataset.id===selMap[provKey];
r.classList.toggle('sel', sel);
});
updateFooter(col, provKey);
col.style.display=visRows.length ? '':'none';
});
if(typeof window.syncTariffCardsVisibility==='function'){
window.syncTariffCardsVisibility();
}}
function updateFooter(col, provKey){
const selId=selMap[provKey];
const foot=col.querySelector('.prov-foot');
const selRow=selId ? col.querySelector(`.t-row[data-id="${selId}"]`):null;
if(!foot||!selRow) return;
foot.style.display='none';
col.querySelectorAll('.t-det-buy').forEach(b=> b.remove());
}
function selectTariffRow(row){
const provKey=row.dataset.prov;
const tid=row.dataset.id;
const col=document.getElementById('col-' + provKey);
if(!col) return;
selMap[provKey]=tid;
userPicked.add(provKey);
col.querySelectorAll('.t-row').forEach(r=> {
r.classList.toggle('sel', r.dataset.id===tid);
});
updateFooter(col, provKey);
}
function toggleDet(tid, btn){
const row=document.querySelector(`.t-row[data-id="${CSS.escape(tid)}"]`);
if(row&&typeof window.openTariffDetails==='function'){
window.openTariffDetails(row);
}}
function updateFilterBadge(){
const badge=document.getElementById('filterFabBadge');
if(!badge) return;
const locationFilterActive = !!(window.RedesimLocationFilter &&
typeof window.RedesimLocationFilter.hasActiveSelection==='function' &&
window.RedesimLocationFilter.hasActiveSelection());
const nonDefault=selDays!==null||selGb!==null||selectedOperatorKeys.size > 0||locationFilterActive;
badge.classList.toggle('active', nonDefault);
}
function collectOperatorFilterItems(){
if(operatorFilterItemsCache){
return operatorFilterItemsCache.slice();
}
const seen=new Set();
const items=[];
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
getRowOperatorItems(row).forEach(item=> {
if(seen.has(item.key)) return;
seen.add(item.key);
items.push(item);
});
});
operatorFilterItemsCache=items.sort((a, b)=> a.name.localeCompare(b.name, 'ru'));
return operatorFilterItemsCache.slice();
}
function getOperatorFilterRoots(){
return Array.from(document.querySelectorAll('[data-operator-filter-root]'));
}
function getOperatorFilterItems(){
return collectOperatorFilterItems();
}
function getOperatorFilterSummary(){
if(!selectedOperatorKeys.size) return 'Все операторы';
const names=getOperatorFilterItems()
.filter(item=> selectedOperatorKeys.has(item.key))
.map(item=> item.name);
if(!names.length) return 'Все операторы';
if(names.length <=2) return names.join(', ');
return `Выбрано: ${names.length}`;
}
function updateOperatorFilterUi(){
const summary=getOperatorFilterSummary();
getOperatorFilterRoots().forEach(root=> {
root.querySelectorAll('[data-operator-key]').forEach(btn=> {
const key=btn.dataset.operatorKey||'';
const active=key==='__all__'
? selectedOperatorKeys.size===0
: selectedOperatorKeys.has(key);
btn.classList.toggle('is-active', active);
btn.setAttribute('aria-checked', active ? 'true':'false');
});
const label=root.querySelector('[data-operator-picker-summary]');
if(label) label.textContent=summary;
const count=root.querySelector('[data-operator-picker-count]');
if(count){
count.textContent=selectedOperatorKeys.size ? String(selectedOperatorKeys.size):'';
count.style.display=selectedOperatorKeys.size ? '':'none';
}});
const modal=document.getElementById('operatorPickerModal');
if(modal&&modal.classList.contains('open')){
const draft=getOperatorPickerDraft();
renderOperatorPickerList(modal, draft);
}}
function applyOperatorFilterChange(){
userPicked.clear();
filterTariffs();
updateFilterBadge();
updateOperatorFilterUi();
if(typeof syncDrawerState==='function'){
syncDrawerState();
}}
function handleOperatorFilterButton(btn){
if(!btn) return;
const key=btn.dataset.operatorKey||'';
if(!key||key==='__all__'){
selectedOperatorKeys.clear();
}else if(selectedOperatorKeys.has(key)){
selectedOperatorKeys.delete(key);
}else{
selectedOperatorKeys.add(key);
}
applyOperatorFilterChange();
}
function getOperatorPickerDraft(){
const modal=document.getElementById('operatorPickerModal');
const raw=modal ? (modal.dataset.draftKeys||''):'';
return new Set(raw.split('|').map(item=> item.trim()).filter(Boolean));
}
function setOperatorPickerDraft(modal, draft){
if(!modal) return;
modal.dataset.draftKeys=Array.from(draft).join('|');
}
function renderOperatorPickerList(modal, draft){
if(!modal) return;
const list=modal.querySelector('[data-operator-picker-list]');
const empty=modal.querySelector('[data-operator-picker-empty]');
const selectedCount=modal.querySelector('[data-operator-picker-selected]');
const search=modal.querySelector('[data-operator-picker-search]');
const query=normalizeOperatorFilterKey(search ? search.value:'');
const items=getOperatorFilterItems();
const filtered=query
? items.filter(item=> item.key.includes(query)||normalizeOperatorFilterKey(item.name).includes(query))
: items;
if(selectedCount) selectedCount.textContent=String(draft.size);
if(!list) return;
list.classList.toggle('is-scrollable', filtered.length > 3);
list.innerHTML=filtered.map(item=> {
const checked=draft.has(item.key);
return `
<div class="operator-picker-item${checked ? ' is-checked':''}" data-operator-picker-option data-operator-key="${escapeInlineTariffHtml(item.key)}" role="checkbox" aria-checked="${checked ? 'true':'false'}">
<span class="operator-picker-check" aria-hidden="true"></span>
<span class="operator-picker-meta">
<span class="operator-picker-name">${escapeInlineTariffHtml(item.name)}</span>
<span class="operator-picker-code">Оператор</span>
</span>
</div>
`;
}).join('');
if(empty) empty.style.display=filtered.length ? 'none':'';
}
function lockOperatorPickerBody(){
if(operatorPickerBodyLocked) return;
operatorPickerScrollY=window.pageYOffset||document.documentElement.scrollTop||0;
document.documentElement.classList.add('operator-picker-lock');
document.body.classList.add('operator-picker-lock');
operatorPickerBodyLocked=true;
}
function unlockOperatorPickerBody(){
document.documentElement.classList.remove('operator-picker-lock');
document.body.classList.remove('operator-picker-lock');
operatorPickerBodyLocked=false;
}
function destroyOperatorPickerModal(){
if(typeof operatorPickerCleanup==='function'){
operatorPickerCleanup();
operatorPickerCleanup=null;
}
const modal=document.getElementById('operatorPickerModal');
if(modal){
modal.remove();
}}
function closeOperatorPicker(){
const modal=document.getElementById('operatorPickerModal');
if(modal){
modal.classList.remove('open');
modal.setAttribute('aria-hidden', 'true');
}
destroyOperatorPickerModal();
unlockOperatorPickerBody();
}
function stabilizeOperatorPickerScroll(modal, savedListScrollTop){
if(!modal) return;
const dialog=modal.querySelector('.operator-picker-dialog');
const list=modal.querySelector('[data-operator-picker-list]');
const active=document.activeElement;
if(active&&modal.contains(active)&&typeof active.blur==='function'){
active.blur();
}
const restore=()=> {
modal.scrollTop=0;
if(dialog) dialog.scrollTop=0;
if(list&&Number.isFinite(savedListScrollTop)) list.scrollTop=savedListScrollTop;
};
restore();
window.requestAnimationFrame(restore);
window.setTimeout(restore, 60);
}
function ensureOperatorPickerModal(){
destroyOperatorPickerModal();
const modal=document.createElement('div');
modal.id='operatorPickerModal';
modal.className='operator-picker-modal';
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML=`
<div class="operator-picker-backdrop" data-operator-picker-close></div>
<div class="operator-picker-dialog" role="dialog" aria-modal="true" aria-labelledby="operatorPickerTitle">
<div class="operator-picker-head">
<h3 class="operator-picker-title" id="operatorPickerTitle">Операторы</h3>
<button type="button" class="operator-picker-close" data-operator-picker-close aria-label="Закрыть">×</button>
</div>
<label class="operator-picker-search">
<span aria-hidden="true">⌕</span>
<input type="search" placeholder="Поиск оператора или кода" autocomplete="off" data-operator-picker-search>
</label>
<div class="operator-picker-list" data-operator-picker-list></div>
<div class="operator-picker-empty" data-operator-picker-empty style="display:none">Операторы не найдены</div>
<div class="operator-picker-foot">
<div class="operator-picker-selected">Выбрано: <span data-operator-picker-selected>0</span></div>
<button type="button" class="operator-picker-reset" data-operator-picker-reset>Сбросить</button>
<button type="button" class="operator-picker-apply" data-operator-picker-apply>Применить</button>
</div>
</div>
`;
document.body.appendChild(modal);
const eventController=typeof AbortController!=='undefined' ? new AbortController():null;
const eventOptions=eventController ? { signal: eventController.signal }:undefined;
operatorPickerCleanup=()=> {
if(eventController){
eventController.abort();
}};
modal.addEventListener('click', e=> {
if(e.target.closest('[data-operator-picker-close]')){
e.preventDefault();
closeOperatorPicker();
return;
}
if(e.target.closest('[data-operator-picker-reset]')){
e.preventDefault();
const draft=new Set();
setOperatorPickerDraft(modal, draft);
renderOperatorPickerList(modal, draft);
return;
}
if(e.target.closest('[data-operator-picker-apply]')){
e.preventDefault();
selectedOperatorKeys.clear();
getOperatorPickerDraft().forEach(key=> selectedOperatorKeys.add(key));
closeOperatorPicker();
applyOperatorFilterChange();
}}, eventOptions);
modal.addEventListener('click', e=> {
const option=e.target.closest('[data-operator-picker-option]');
if(!option||!modal.contains(option)) return;
e.preventDefault();
const list=modal.querySelector('[data-operator-picker-list]');
const savedListScrollTop=list ? list.scrollTop:0;
const draft=getOperatorPickerDraft();
const key=option.dataset.operatorKey||'';
if(!key) return;
if(draft.has(key)){
draft.delete(key);
option.classList.remove('is-checked');
option.setAttribute('aria-checked', 'false');
}else{
draft.add(key);
option.classList.add('is-checked');
option.setAttribute('aria-checked', 'true');
}
setOperatorPickerDraft(modal, draft);
const selectedCount=modal.querySelector('[data-operator-picker-selected]');
if(selectedCount) selectedCount.textContent=String(draft.size);
stabilizeOperatorPickerScroll(modal, savedListScrollTop);
}, eventOptions);
modal.addEventListener('input', e=> {
if(!e.target.matches('[data-operator-picker-search]')) return;
renderOperatorPickerList(modal, getOperatorPickerDraft());
}, eventOptions);
document.addEventListener('keydown', e=> {
if(e.key==='Escape'&&modal.classList.contains('open')){
closeOperatorPicker();
}}, eventOptions);
if(window.visualViewport){
window.visualViewport.addEventListener('resize', ()=> updateOperatorPickerViewport(modal), eventOptions);
window.visualViewport.addEventListener('scroll', ()=> updateOperatorPickerViewport(modal), eventOptions);
}else{
window.addEventListener('resize', ()=> updateOperatorPickerViewport(modal), eventOptions);
}
return modal;
}
function updateOperatorPickerViewport(modal){
if(!modal) return;
const vv=window.visualViewport;
const isCompact=window.matchMedia&&window.matchMedia('(max-width: 767px)').matches;
const layoutHeight=Math.max(window.innerHeight||0, document.documentElement.clientHeight||0);
const layoutWidth=Math.max(window.innerWidth||0, document.documentElement.clientWidth||0);
const visualHeight=vv&&vv.height ? vv.height:layoutHeight;
const visualWidth=vv&&vv.width ? vv.width:layoutWidth;
const visualTop=vv&&typeof vv.offsetTop==='number' ? vv.offsetTop:0;
const keyboardOffset=isCompact
? Math.max(0, Math.round(layoutHeight - visualHeight - visualTop))
: 0;
const modalHeight=isCompact ? layoutHeight:visualHeight;
modal.style.setProperty('--operator-picker-vh', `${Math.round(modalHeight)}px`);
modal.style.setProperty('--operator-picker-visual-vh', `${Math.round(visualHeight)}px`);
modal.style.setProperty('--operator-picker-keyboard-offset', `${keyboardOffset}px`);
modal.style.setProperty('--operator-picker-top', '0px');
modal.style.setProperty('--operator-picker-left', '0px');
modal.style.setProperty('--operator-picker-width', `${Math.round(isCompact ? layoutWidth:visualWidth)}px`);
}
function openOperatorPicker(){
const modal=ensureOperatorPickerModal();
const draft=new Set(selectedOperatorKeys);
const search=modal.querySelector('[data-operator-picker-search]');
setOperatorPickerDraft(modal, draft);
if(search) search.value='';
updateOperatorPickerViewport(modal);
renderOperatorPickerList(modal, draft);
const dialog=modal.querySelector('.operator-picker-dialog');
const list=modal.querySelector('[data-operator-picker-list]');
modal.scrollTop=0;
if(dialog) dialog.scrollTop=0;
if(list) list.scrollTop=0;
lockOperatorPickerBody();
modal.classList.add('open');
modal.setAttribute('aria-hidden', 'false');
updateOperatorPickerViewport(modal);
window.requestAnimationFrame(()=> {
modal.scrollTop=0;
if(dialog) dialog.scrollTop=0;
if(list) list.scrollTop=0;
if(!modal.querySelector('.operator-picker-item')&&getOperatorFilterItems().length){
renderOperatorPickerList(modal, getOperatorPickerDraft());
}});
const isCompact=window.matchMedia&&window.matchMedia('(max-width: 767px)').matches;
if(!isCompact){
window.setTimeout(()=> {
const input=modal.querySelector('[data-operator-picker-search]');
if(input) input.focus({ preventScroll: true });
}, 30);
}}
function buildOperatorFilter(){
const pickerCard=document.querySelector('.picker--sidebar .picker-card');
if(!pickerCard||document.getElementById('operatorFilterWrap')) return;
const items=getOperatorFilterItems();
if(!items.length) return;
const wrap=document.createElement('div');
wrap.className='operator-filter-wrap';
wrap.id='operatorFilterWrap';
wrap.dataset.operatorFilterRoot='1';
wrap.innerHTML=`
<hr class="picker-sep operator-filter-sep">
<div class="step-lbl operator-filter-title">Операторы</div>
<button type="button" class="operator-filter-open" data-operator-picker-open aria-haspopup="dialog">
<span class="operator-filter-open-text" data-operator-picker-summary>Все операторы</span>
<span class="operator-filter-open-count" data-operator-picker-count style="display:none"></span>
</button>
`;
pickerCard.appendChild(wrap);
updateOperatorFilterUi();
}
function collectVolumeOptions(){
const seen=new Set();
const values=[];
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
if(isDaily(row)) return;
const gb=parseInt(row.dataset.volume, 10);
if(!gb||seen.has(gb)) return;
seen.add(gb);
values.push(gb);
});
return values.sort((a, b)=> a - b);
}
function buildGbChips(){
const gbChips=document.getElementById('gbChips');
if(!gbChips||gbChips.dataset.built==='1') return;
const values=collectVolumeOptions();
const hasDaily=Array.from(document.querySelectorAll('.prov-col .t-row')).some(isDaily);
if(!values.length&&!hasDaily) return;
gbChips.dataset.built='1';
const unlimitedChip=hasDaily
? `<button class="dc" type="button" data-gb="unlimited">Безлимит</button>`
: '';
const numericChips=values.map(v=>
`<button class="dc" type="button" data-gb="${v}">${v} ГБ</button>`
).join('');
gbChips.innerHTML=unlimitedChip + numericChips;
}
function collectPeriodOptions(){
const seen=new Set();
const values=[];
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
if(isDaily(row)) return;
const period=parseInt(row.dataset.period, 10);
if(!period||seen.has(period)) return;
seen.add(period);
values.push(period);
});
return values.sort((a, b)=> a - b);
}
const DAY_CHIP_ICON='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>';
function buildDayChips(){
const dayChips=document.getElementById('dayChips');
if(!dayChips||dayChips.dataset.built==='1') return;
const values=collectPeriodOptions();
if(!values.length) return;
dayChips.dataset.built='1';
dayChips.innerHTML=values.map(v=>
`<button class="dc dc--icon" type="button" data-d="${v}">${DAY_CHIP_ICON}${v} ${dW(v)}</button>`
).join('');
}
function enableOperatorHorizontalDragScroll(scroller){
if(!scroller||scroller.dataset.operatorDragScrollReady==='1') return;
scroller.dataset.operatorDragScrollReady='1';
let pointerId=null;
let startX=0;
let startY=0;
let startScrollLeft=0;
let isPointerDown=false;
let isDragging=false;
let suppressClick=false;
const finishDrag=()=> {
if(!isPointerDown) return;
isPointerDown=false;
if(isDragging){
suppressClick=true;
scroller.classList.remove('is-dragging');
window.setTimeout(()=> {
suppressClick=false;
}, 180);
}
isDragging=false;
pointerId=null;
};
scroller.addEventListener('pointerdown', e=> {
if(scroller.classList.contains('operator-filter-row')&&e.target.closest('.operator-filter-chip')){
return;
}
if(e.pointerType!=='mouse') return;
if(e.button!==0) return;
if(scroller.scrollWidth <=scroller.clientWidth + 2) return;
pointerId=e.pointerId;
isPointerDown=true;
isDragging=false;
startX=e.clientX;
startY=e.clientY;
startScrollLeft=scroller.scrollLeft;
try {
scroller.setPointerCapture(pointerId);
} catch (err){ }});
scroller.addEventListener('pointermove', e=> {
if(!isPointerDown||e.pointerId!==pointerId) return;
const dx=e.clientX - startX;
const dy=e.clientY - startY;
if(!isDragging){
if(Math.abs(dx) < 5) return;
if(Math.abs(dx) < Math.abs(dy) + 3) return;
isDragging=true;
scroller.classList.add('is-dragging');
}
scroller.scrollLeft=startScrollLeft - dx;
e.preventDefault();
}, { passive: false });
scroller.addEventListener('pointerup', finishDrag);
scroller.addEventListener('pointercancel', finishDrag);
scroller.addEventListener('lostpointercapture', finishDrag);
scroller.addEventListener('click', e=> {
if(!suppressClick) return;
e.preventDefault();
e.stopPropagation();
}, true);
}
function initOperatorHorizontalDragScroll(root){
const scope=root||document;
scope.querySelectorAll('.operator-filter-row').forEach(enableOperatorHorizontalDragScroll);
}
document.addEventListener('DOMContentLoaded', function (){
document.querySelectorAll('.prov-col .t-row').forEach(row=> {
row.addEventListener('click', function (e){
e.preventDefault();
if(typeof window.openCheckoutModalFromRow==='function'){
window.openCheckoutModalFromRow(row);
return;
}
if(typeof window.openTariffDetails==='function'){
window.openTariffDetails(row);
return;
}
selectTariffRow(row);
});
});
if(!document.getElementById('dayChips')) return;
const dayChips=document.getElementById('dayChips');
const gbChips=document.getElementById('gbChips');
const dayInp=document.getElementById('dayInp');
const dayWord=document.getElementById('dayWord');
if(!dayChips||!dayInp) return;
buildGbChips();
buildDayChips();
dayChips.addEventListener('click', e=> {
const b=e.target.closest('[data-d]');
if(!b) return;
const isActive=b.classList.contains('act');
document.querySelectorAll('#dayChips [data-d]').forEach(x=> x.classList.remove('act'));
if(isActive){
selDays=null;
dayInp.value='';
if(dayWord) dayWord.textContent='дней';
}else{
b.classList.add('act');
selDays=parseInt(b.dataset.d, 10);
dayInp.value=selDays;
if(dayWord) dayWord.textContent=dW(selDays);
}
userPicked.clear();
filterTariffs();
updateFilterBadge();
syncDrawerState();
});
if(gbChips){
gbChips.addEventListener('click', e=> {
const b=e.target.closest('[data-gb]');
if(!b) return;
const isActive=b.classList.contains('act');
document.querySelectorAll('#gbChips [data-gb]').forEach(x=> x.classList.remove('act'));
if(isActive){
selGb=null;
}else{
b.classList.add('act');
selGb=b.dataset.gb==='unlimited' ? 'unlimited':parseInt(b.dataset.gb, 10);
}
userPicked.clear();
filterTariffs();
updateFilterBadge();
syncDrawerState();
});
}
dayInp.addEventListener('input', e=> {
const raw=e.target.value.trim();
if(raw===''){
selDays=null;
if(dayWord) dayWord.textContent='дней';
document.querySelectorAll('#dayChips [data-d]').forEach(x=> x.classList.remove('act'));
userPicked.clear();
scheduleFilter();
syncDrawerState();
return;
}
let v=parseInt(raw, 10);
if(isNaN(v)) return;
if(v < 1) v=1;
if(v > 180) v=180;
e.target.value=v;
selDays=v;
if(dayWord) dayWord.textContent=dW(v);
document.querySelectorAll('#dayChips [data-d]').forEach(x=> x.classList.remove('act'));
userPicked.clear();
scheduleFilter();
syncDrawerState();
});
buildOperatorFilter();
initOperatorHorizontalDragScroll(document);
document.addEventListener('click', function (e){
const pickerOpen=e.target.closest('[data-operator-picker-open]');
if(pickerOpen){
e.preventDefault();
openOperatorPicker();
return;
}
const chip=e.target.closest('.operator-filter-chip');
if(!chip||chip.closest('#filterDrawerBody')) return;
e.preventDefault();
handleOperatorFilterButton(chip);
});
const stickyFilterBtn=document.getElementById('stickyFilterBtn');
const filterFabBtn=document.getElementById('filterFabBtn');
const filterDrawer=document.getElementById('filterDrawer');
const filterDrawerOverlay=document.getElementById('filterDrawerOverlay');
const filterDrawerClose=document.getElementById('filterDrawerClose');
const filterDrawerApply=document.getElementById('filterDrawerApply');
const filterDrawerBody=document.getElementById('filterDrawerBody');
const pickerEl=document.querySelector('.picker--sidebar');
if(pickerEl&&filterDrawerBody){
const pickerCard=pickerEl.querySelector('.picker-card');
if(pickerCard){
const clone=pickerCard.cloneNode(true);
const cloneTitle=clone.querySelector('.picker-side-title');
if(cloneTitle) cloneTitle.remove();
clone.querySelectorAll('[id]').forEach(el=> {
el.dataset.drawerOrigin=el.id;
el.id='drawer-' + el.id;
});
clone.querySelectorAll('[for]').forEach(el=> {
el.setAttribute('for', 'drawer-' + el.getAttribute('for'));
});
filterDrawerBody.appendChild(clone);
initOperatorHorizontalDragScroll(filterDrawerBody);
}}
function syncDrawerState(){
if(!filterDrawerBody) return;
filterDrawerBody.querySelectorAll('[data-d]').forEach(b=> {
b.classList.toggle('act', selDays!==null&&parseInt(b.dataset.d, 10)===selDays);
});
const dInp=filterDrawerBody.querySelector('#drawer-dayInp');
if(dInp) dInp.value=selDays!==null ? selDays:'';
const dWord=filterDrawerBody.querySelector('#drawer-dayWord');
if(dWord) dWord.textContent=selDays!==null ? dW(selDays):'дней';
filterDrawerBody.querySelectorAll('[data-gb]').forEach(b=> {
const orig=document.querySelector(`#gbChips [data-gb="${CSS.escape(b.dataset.gb)}"]`);
b.classList.toggle('act', !!(orig&&orig.classList.contains('act')));
});
const checkedRadio=document.querySelector('input[name="locFilter"]:checked');
if(checkedRadio){
filterDrawerBody.querySelectorAll('input[name="locFilter"]').forEach(r=> {
r.checked=r.value===checkedRadio.value;
});
}
filterDrawerBody.querySelectorAll('.lf-check').forEach(lbl=> {
const inp=lbl.querySelector('input[name="locFilter"]');
if(inp) lbl.classList.toggle('act', inp.checked);
});
if(window.RedesimLocationFilter&&typeof window.RedesimLocationFilter.syncInputs==='function'){
window.RedesimLocationFilter.syncInputs();
}
updateOperatorFilterUi();
}
if(filterDrawerBody){
filterDrawerBody.addEventListener('click', e=> {
const operatorPickerOpen=e.target.closest('[data-operator-picker-open]');
if(operatorPickerOpen){
e.preventDefault();
openOperatorPicker();
return;
}
const operatorChip=e.target.closest('.operator-filter-chip');
if(operatorChip){
e.preventDefault();
handleOperatorFilterButton(operatorChip);
return;
}
const gc=e.target.closest('[data-gb]');
if(gc){
const orig=document.querySelector(`#gbChips [data-gb="${CSS.escape(gc.dataset.gb)}"]`);
if(orig) orig.click();
return;
}
const dc=e.target.closest('[data-d]');
if(dc){
const orig=document.querySelector(`#dayChips [data-d="${dc.dataset.d}"]`);
if(orig) orig.click();
return;
}
const adj=e.target.closest('.day-adj[data-delta]');
if(adj){
adjustDays(parseInt(adj.dataset.delta, 10));
return;
}});
filterDrawerBody.addEventListener('input', e=> {
const inp=e.target.closest('#drawer-dayInp');
if(!inp) return;
const orig=document.getElementById('dayInp');
if(orig){
orig.value=inp.value;
orig.dispatchEvent(new Event('input'));
}});
}
if(pickerEl&&stickyFilterBtn){
new IntersectionObserver(([entry])=> {
stickyFilterBtn.classList.toggle('visible', !entry.isIntersecting);
}, { threshold: 0.1 }).observe(pickerEl);
}
function openFilterDrawer(){
syncDrawerState();
filterDrawer?.classList.add('open');
filterDrawerOverlay?.classList.add('open');
document.body.style.overflow='hidden';
}
function closeFilterDrawer(){
filterDrawer?.classList.remove('open');
filterDrawerOverlay?.classList.remove('open');
document.body.style.overflow='';
}
if(filterFabBtn) filterFabBtn.addEventListener('click', openFilterDrawer);
if(filterDrawerClose) filterDrawerClose.addEventListener('click', closeFilterDrawer);
if(filterDrawerOverlay) filterDrawerOverlay.addEventListener('click', closeFilterDrawer);
if(filterDrawerApply){
filterDrawerApply.addEventListener('click', ()=> {
closeFilterDrawer();
document.getElementById('results')?.scrollIntoView({ behavior: 'smooth' });
});
}
if(filterDrawer){
let touchStartY=0;
filterDrawer.addEventListener('touchstart', e=> {
touchStartY=e.touches[0].clientY;
}, { passive: true });
filterDrawer.addEventListener('touchend', e=> {
if(e.changedTouches[0].clientY - touchStartY > 80) closeFilterDrawer();
}, { passive: true });
}
filterTariffs();
updateFilterBadge();
});
window.adjustDays=adjustDays;
window.toggleDet=toggleDet;
window.filterTariffs=filterTariffs;
})();
(function (){
const banner=document.getElementById('promoBanner');
if(!banner) return;
fetch('/api/v2/promo/favorite', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }})
.then(r=> r.ok ? r.json():null)
.then(res=> {
if(!res||!res.success||!res.data) return;
const { code, comment, date }=res.data;
const promoTitle=document.getElementById('promoTitle');
const promoCode=document.getElementById('promoCode');
if(promoTitle) promoTitle.textContent=comment||'';
if(promoCode) promoCode.textContent=(code||'').toUpperCase();
banner.style.display='';
if(!date) return;
const endTs=new Date(date.replace(' ', 'T') + '+03:00').getTime();
if(isNaN(endTs)) return;
if(Date.now() >=endTs){
banner.style.display='none';
return;
}
const timerEl=document.getElementById('promoTimer');
const dEl=document.getElementById('timerD');
const hEl=document.getElementById('timerH');
const mEl=document.getElementById('timerM');
const sEl=document.getElementById('timerS');
if(!timerEl||!dEl||!hEl||!mEl||!sEl) return;
const dBlock=dEl.parentElement;
const dSep=dBlock ? dBlock.nextElementSibling:null;
const totalSec=Math.floor((endTs - Date.now()) / 1000);
const showDays=totalSec >=86400;
if(!showDays){
if(dBlock) dBlock.style.display='none';
if(dSep) dSep.style.display='none';
}
const pad=n => String(n).padStart(2, '0');
function tick(){
const diff=endTs - Date.now();
if(diff <=0){
banner.style.display='none';
clearInterval(iv);
return;
}
const s=Math.floor(diff / 1000);
if(showDays){
dEl.textContent=Math.floor(s / 86400);
}
hEl.textContent=pad(Math.floor((s % 86400) / 3600));
mEl.textContent=pad(Math.floor((s % 3600) / 60));
sEl.textContent=pad(s % 60);
}
tick();
timerEl.style.display='';
const iv=setInterval(tick, 1000);
})
.catch(()=> { });
})();
(function (){
const input=document.getElementById('countrySearch');
if(!input) return;
const grid=document.getElementById('countriesGrid');
const extra=document.getElementById('countriesExtra');
const clearBtn=document.getElementById('countrySearchClear');
const emptyEl=document.getElementById('countrySearchEmpty');
if(!grid) return;
const allCards=[
...grid.querySelectorAll('.country-card'),
...(extra ? extra.querySelectorAll('.country-card'):[])
];
const norm=s => (s||'').toLowerCase().replace(/ё/g, 'е').trim();
const index=allCards.map(card=> ({
el: card,
name: norm(card.querySelector('.country-card-name')?.textContent)
}));
function filter(q){
const query=norm(q);
clearBtn.classList.toggle('show', query.length > 0);
if(!query){
allCards.forEach(c=> c.classList.remove('country-hidden'));
if(extra) extra.classList.remove('open');
emptyEl.classList.remove('show');
return;
}
if(extra) extra.classList.add('open');
let visible=0;
index.forEach(({ el, name })=> {
const match=name.includes(query);
el.classList.toggle('country-hidden', !match);
if(match) visible++;
});
emptyEl.classList.toggle('show', visible===0);
}
let t=null;
input.addEventListener('input', e=> {
clearTimeout(t);
t=setTimeout(()=> filter(e.target.value), 80);
});
clearBtn.addEventListener('click', ()=> {
input.value='';
filter('');
input.focus();
});
input.addEventListener('keydown', e=> {
if(e.key==='Escape'&&input.value){
input.value='';
filter('');
}});
})();
(function (){
const provCols=document.getElementById('provCols----');
if(!provCols) return;
function normalizeListValue(value){
return String(value||'')
.split(',')
.map(item=> item.trim())
.filter(Boolean);
}
function mergeUniqueCommaValues(){
const seen=new Set();
const result=[];
Array.from(arguments).forEach(value=> {
normalizeListValue(value).forEach(item=> {
const key=item.toLowerCase();
if(seen.has(key)) return;
seen.add(key);
result.push(item);
});
});
return result.join(', ');
}
function mergeProviderColumns(root, targetKey, sourceKey){
if(!root) return;
const targetCol=root.querySelector('#col-' + targetKey);
const sourceCol=root.querySelector('#col-' + sourceKey);
if(!targetCol||!sourceCol||targetCol===sourceCol) return;
const targetRows=targetCol.querySelector('.tariff-rows');
const sourceRows=sourceCol.querySelector('.tariff-rows');
if(!targetRows||!sourceRows) return;
targetCol.dataset.providerOperators=mergeUniqueCommaValues(
targetCol.dataset.providerOperators,
sourceCol.dataset.providerOperators
);
targetCol.dataset.providerSpeed=mergeUniqueCommaValues(
String(targetCol.dataset.providerSpeed||'').replace(/\//g, ','),
String(sourceCol.dataset.providerSpeed||'').replace(/\//g, ',')
).replace(/,\s*/g, '/');
targetCol.dataset.providerName=targetCol.dataset.providerName||'Alfa';
Array.from(sourceRows.children).forEach(card=> {
const row=card.querySelector('.t-row');
if(row){
if(!row.dataset.originalProv){
row.dataset.originalProv=row.dataset.prov||sourceKey;
}
row.dataset.prov=targetKey;
}
targetRows.appendChild(card);
});
sourceCol.remove();
}
mergeProviderColumns(provCols, 'a', 'p');
const providerCols=Array.from(provCols.querySelectorAll('.prov-col'));
if(!providerCols.length) return;
function getRowCard(row){
return row.closest('.tariff-card')||row;
}
function hasVisibleRows(col){
const rows=Array.from(col.querySelectorAll('.t-row'));
return rows.some(row=> {
const card=getRowCard(row);
const rowStyle=window.getComputedStyle(row);
const cardStyle=window.getComputedStyle(card);
return (
!row.classList.contains('lf-hidden') &&
!row.classList.contains('js-hidden') &&
!card.classList.contains('is-hidden') &&
!row.hidden &&
rowStyle.display!=='none' &&
cardStyle.display!=='none'
);
});
}
function initProviderColumn(){
providerCols.forEach(col=> col.classList.add('provider-active'));
provCols.classList.add('provider-tabbed');
}
function refreshActiveProvider(){
const activeCol=providerCols.find(col=> col.classList.contains('provider-active'))||providerCols[0];
if(activeCol&&window.RedesimLocationFilter&&typeof window.RedesimLocationFilter.syncForActiveProvider==='function'){
window.RedesimLocationFilter.syncForActiveProvider();
}}
function syncTariffCardsVisibility(){
document.querySelectorAll('.tariff-card').forEach(function (card){
const row=card.querySelector('.t-row');
if(!row){
card.classList.add('is-hidden');
return;
}
const rowStyle=window.getComputedStyle(row);
const isHidden =
row.classList.contains('lf-hidden') ||
row.classList.contains('js-hidden') ||
row.hidden ||
rowStyle.display==='none' ||
card.style.display==='none';
card.classList.toggle('is-hidden', isHidden);
});
}
function parseLocation(row){
const raw=row.dataset.locationCities||'';
if(!raw) return '';
try {
const items=JSON.parse(raw);
if(Array.isArray(items)){
return items
.map(item=> item&&item.name ? item.name:'')
.filter(Boolean)
.join(', ');
}} catch (e){ }
return '';
}
function normalizeNetworkName(value){
if(!value) return '';
let str=String(value).trim();
if((str.startsWith('"')&&str.endsWith('"')) ||
(str.startsWith("'")&&str.endsWith("'"))
){
try {
str=JSON.parse(str);
} catch (e){
str=str.slice(1, -1);
}}
return String(str||'').trim();
}
function getNetworkList(row){
const ownNetwork=normalizeNetworkName(row.dataset.networkList||'');
if(ownNetwork) return ownNetwork;
const col=row.closest('.prov-col');
return col ? normalizeNetworkName(col.dataset.providerOperators||''):'';
}
function splitTariffList(value){
const seen=new Set();
const items=[];
String(value||'')
.split(',')
.map(item=> item.trim())
.filter(Boolean)
.forEach(item=> {
const key=item.toLowerCase();
if(seen.has(key)) return;
seen.add(key);
items.push(item);
});
return items;
}
function getTariffCountryItems(row){
if(!row||!row.dataset) return [];
const raw=row.dataset.locationCities||'';
const items=[];
const seen=new Set();
if(raw){
try {
const parsed=JSON.parse(raw);
if(Array.isArray(parsed)){
parsed.forEach(item=> {
const name=item&&item.name ? String(item.name).trim():'';
if(!name) return;
const key=name.toLowerCase();
if(seen.has(key)) return;
seen.add(key);
items.push(name);
});
}} catch (e){ }}
if(items.length) return items;
return splitTariffList(row.dataset.location||'');
}
function getTariffOperatorItems(row){
return splitTariffList(getNetworkList(row));
}
let tariffCoverageModal=null;
function ensureTariffCoverageModal(){
if(tariffCoverageModal) return tariffCoverageModal;
tariffCoverageModal=document.createElement('div');
tariffCoverageModal.id='tariffCoverageModal';
tariffCoverageModal.className='tariff-coverage-modal';
tariffCoverageModal.setAttribute('aria-hidden', 'true');
tariffCoverageModal.innerHTML=`
<div class="tariff-coverage-backdrop" data-tariff-coverage-close></div>
<div class="tariff-coverage-dialog" role="dialog" aria-modal="true" aria-labelledby="tariffCoverageTitle">
<button type="button" class="tariff-coverage-close" data-tariff-coverage-close aria-label="Закрыть">×</button>
<div class="tariff-coverage-eyebrow">Покрытие тарифа</div>
<h3 class="tariff-coverage-title" id="tariffCoverageTitle">Страны и операторы</h3>
<div class="tariff-coverage-groups" data-tariff-coverage-groups></div>
</div>
`;
document.body.appendChild(tariffCoverageModal);
tariffCoverageModal.addEventListener('click', function (e){
if(e.target.closest('[data-tariff-coverage-close]')){
closeTariffCoverageModal();
}});
document.addEventListener('keydown', function (e){
if(e.key==='Escape'&&tariffCoverageModal.classList.contains('open')){
closeTariffCoverageModal();
}});
return tariffCoverageModal;
}
function renderTariffCoverageGroup(title, items, emptyText){
const list=items&&items.length ? items:[];
return `
<section class="tariff-coverage-group">
<div class="tariff-coverage-group-head">
<span>${escapeHtml(title)}</span>
<b>${list.length}</b>
</div>
${list.length
? `<div class="tariff-coverage-list">${list.map(item=> `<span>${escapeHtml(item)}</span>`).join('')}</div>`
: `<div class="tariff-coverage-empty">${escapeHtml(emptyText||'Информация уточняется')}</div>`}
</section>
`;
}
function escapeHtml(str){
return String(str||'')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function openTariffCoverageModal(row, focusKind){
if(!row) return;
const countries=getTariffCountryItems(row);
const operators=getTariffOperatorItems(row);
const modal=ensureTariffCoverageModal();
const groups=modal.querySelector('[data-tariff-coverage-groups]');
if(!groups) return;
const countriesHtml=renderTariffCoverageGroup('Страны действия', countries, 'Страны действия уточняются');
const operatorsHtml=renderTariffCoverageGroup('Операторы', operators, 'Операторы уточняются');
groups.innerHTML=focusKind==='operators'
? operatorsHtml + countriesHtml
: countriesHtml + operatorsHtml;
modal.classList.add('open');
modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('tariff-coverage-lock');
}
function closeTariffCoverageModal(){
if(!tariffCoverageModal) return;
tariffCoverageModal.classList.remove('open');
tariffCoverageModal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('tariff-coverage-lock');
}
function updateTariffCardCoverageSummaries(context){
const root=context||document;
root.querySelectorAll('.tariff-card .t-row').forEach(function (row){
const operatorsEl=row.querySelector('.t-operators');
if(!operatorsEl) return;
const countries=getTariffCountryItems(row);
const operators=getTariffOperatorItems(row);
const shouldShowSummary=countries.length > 4||operators.length > 4;
if(!shouldShowSummary) return;
operatorsEl.classList.add('t-operators--summary');
operatorsEl.innerHTML=`
<span class="tariff-card-coverage-summary">
<button type="button" class="tariff-card-coverage-link" data-tariff-coverage-kind="countries">Стран: ${countries.length}</button>,
<button type="button" class="tariff-card-coverage-link" data-tariff-coverage-kind="operators">Операторы: ${operators.length}</button>
</span>
`;
});
}
document.addEventListener('click', function (e){
const btn=e.target.closest&&e.target.closest('.tariff-card-coverage-link');
if(!btn) return;
const row=btn.closest('.t-row');
if(!row) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
openTariffCoverageModal(row, btn.dataset.tariffCoverageKind||'countries');
}, true);
function getPackageDetails(row){
const card=getRowCard(row);
const rows=Array.from(card.querySelectorAll('.det-table tr'));
return rows.map(tr=> {
const cells=tr.querySelectorAll('td');
return {
label: cells[0] ? cells[0].textContent.trim():'',
value: cells[1] ? cells[1].textContent.trim():''
};}).filter(item=> item.label||item.value);
}
function shouldCollapseTariffText(value){
const text=String(value||'').trim();
if(!text) return false;
const parts=text.split(',').map(item=> item.trim()).filter(Boolean);
return text.length > 135||parts.length > 8;
}
function getElementPlainText(el){
if(!el) return '';
const expanded=el.querySelector('.tariff-expandable-text');
if(expanded) return expanded.dataset.fullText||expanded.textContent||'';
return el.textContent||'';
}
function setExpandableTariffText(el, value, fallback){
if(!el) return;
const text=String(value||fallback||'').trim();
const safeText=escapeHtml(text||fallback||'уточняется');
if(!shouldCollapseTariffText(text)){
el.classList.remove('has-tariff-expandable-text');
el.innerHTML=safeText;
return;
}
el.classList.add('has-tariff-expandable-text');
el.innerHTML=`
<span class="tariff-expandable-text is-collapsed" data-full-text="${safeText}">${safeText}</span>
<button class="tariff-expandable-btn" type="button" data-tariff-text-toggle aria-expanded="false">
Показать полностью
</button>
`;
}
function applyCheckoutModalSpecCollapse(){
const specEl=document.getElementById('mSpec');
if(!specEl) return;
if(specEl.classList.contains('modal-tariff-spec--enhanced')||specEl.dataset.checkoutEnhanced==='1'){
return;
}
const text=getElementPlainText(specEl).trim();
if(!text){
specEl.classList.remove('has-tariff-expandable-text');
specEl.removeAttribute('data-tariff-spec-collapsed-text');
return;
}
if(specEl.classList.contains('has-tariff-expandable-text') &&
specEl.dataset.tariffSpecCollapsedText===text
){
return;
}
if(!shouldCollapseTariffText(text)){
specEl.classList.remove('has-tariff-expandable-text');
specEl.removeAttribute('data-tariff-spec-collapsed-text');
specEl.textContent=text;
return;
}
specEl.dataset.tariffSpecCollapsedText=text;
setExpandableTariffText(specEl, text, text);
}
function initCheckoutModalSpecCollapse(){
const specEl=document.getElementById('mSpec');
if(!specEl) return;
applyCheckoutModalSpecCollapse();
const observer=new MutationObserver(function (){
window.requestAnimationFrame(applyCheckoutModalSpecCollapse);
});
observer.observe(specEl, {
childList: true,
characterData: true,
subtree: true
});
}
function applyInlineTariffTextCollapse(context){
const root=context||document;
function isLongListLabel(label){
const normalized=String(label||'').trim().toLowerCase();
return normalized.includes('оператор') ||
normalized.includes('сеть') ||
normalized.includes('стра') ||
normalized.includes('зона') ||
normalized.includes('действ');
}
root.querySelectorAll('.det-table tr').forEach(function (tr){
const cells=tr.querySelectorAll('td');
if(cells.length < 2||!isLongListLabel(cells[0].textContent)) return;
const valueCell=cells[1];
const text=getElementPlainText(valueCell).trim();
if(!shouldCollapseTariffText(text)) return;
setExpandableTariffText(valueCell, text, text);
});
root.querySelectorAll('.tariff-details-list-row').forEach(function (row){
const labelEl=row.querySelector('span');
const valueEl=row.querySelector('strong');
if(!labelEl||!valueEl||!isLongListLabel(labelEl.textContent)) return;
const text=getElementPlainText(valueEl).trim();
if(!shouldCollapseTariffText(text)) return;
setExpandableTariffText(valueEl, text, text);
});
}
function getTariffModalData(row){
const col=row.closest('.prov-col');
const countryWrap=document.querySelector('.prov-cols[data-country-code]');
const countryCode=countryWrap ? countryWrap.dataset.countryCode:'';
const countryName=countryWrap ? countryWrap.dataset.countryName:'';
const name=row.querySelector('.t-name-main')?.textContent.trim()||'Тариф';
const price=row.querySelector('.t-price')?.textContent.trim()||'';
const ppg=row.querySelector('.t-ppg')?.textContent.trim()||'';
const speed=col ? (col.dataset.providerSpeed||''):'';
const operators=getNetworkList(row);
const location=parseLocation(row)||countryName||row.dataset.location||'';
return {
name,
price,
ppg,
speed,
operators,
location,
countryName,
details: getPackageDetails(row),
packageCode: row.dataset.id||'',
countryCode,
days: row.dataset.volume&&parseInt(row.dataset.volume, 10) >=999999999999
? parseInt(document.getElementById('dayInp')?.value||'0', 10)
: 0
};}
function getTariffCheckoutDataFromRow(row){
if(!row) return null;
const data=getTariffModalData(row);
const checkoutName=data.countryName||data.location||data.name||'Тариф';
return {
name: checkoutName,
specTitle: data.name,
spec: [data.name, data.speed].filter(Boolean).join(' · '),
price: data.price,
packageCode: data.packageCode,
countryCode: data.countryCode,
days: data.days,
location: data.location,
operators: data.operators,
speed: data.speed,
ppg: data.ppg,
details: data.details
};}
function openCheckoutModalFromRow(row){
if(!row||typeof openModal!=='function') return false;
const modalData=getTariffCheckoutDataFromRow(row);
if(!modalData) return false;
openModal(modalData);
return true;
}
function openTariffDetails(row){
const modal=document.getElementById('tariffDetailsModal');
if(!modal||!row) return;
const data=getTariffModalData(row);
const titleEl=document.getElementById('tdTitle');
const priceEl=document.getElementById('tdPrice');
const ppgEl=document.getElementById('tdPpg');
const locationEl=document.getElementById('tdLocation');
const networkEl=document.getElementById('tdNetwork');
const locationWrap=document.getElementById('tdLocationWrap');
const networkWrap=document.getElementById('tdNetworkWrap');
const detailsEl=document.getElementById('tdDetails');
const buyBtn=document.getElementById('tdBuyBtn');
if(titleEl) titleEl.textContent=data.name;
if(priceEl) priceEl.textContent=data.price||'—';
if(ppgEl) ppgEl.textContent=data.ppg||'';
setExpandableTariffText(locationEl, data.location, 'уточняется');
setExpandableTariffText(networkEl, data.operators, 'уточняются');
if(locationWrap) locationWrap.style.display=data.location ? '':'none';
if(networkWrap) networkWrap.style.display=data.operators ? '':'none';
if(detailsEl){
detailsEl.innerHTML=data.details.length
? data.details.map(item=> `
<div class="tariff-details-list-row">
<span>${escapeHtml(item.label)}</span>
<strong>${escapeHtml(item.value)}</strong>
</div>
`).join('')
: '<div class="tariff-details-list-row"><span>Информация</span><strong>уточняется</strong></div>';
}
if(detailsEl){
applyInlineTariffTextCollapse(detailsEl);
}
if(buyBtn){
buyBtn.onclick=function (){
if(typeof openModal!=='function') return;
const modalData=getTariffCheckoutDataFromRow(row);
closeTariffDetails();
openModal(modalData);
};}
modal.classList.add('open');
modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('tariff-details-lock');
if(window.RedesimTariffHash&&data.packageCode){
window.RedesimTariffHash.set(data.packageCode, data.days||0);
}}
function closeTariffDetails(){
const modal=document.getElementById('tariffDetailsModal');
if(!modal) return;
modal.classList.remove('open');
modal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('tariff-details-lock');
if(window.RedesimTariffHash){
window.RedesimTariffHash.clear();
}}
function initTariffDetailsModal(){
document.addEventListener('click', function (e){
const closeBtn=e.target.closest('[data-tariff-details-close]');
if(closeBtn){
e.preventDefault();
closeTariffDetails();
}});
document.addEventListener('click', function (e){
const toggleBtn=e.target.closest('[data-tariff-text-toggle]');
if(!toggleBtn) return;
e.preventDefault();
e.stopPropagation();
const box=toggleBtn.previousElementSibling;
if(!box||!box.classList.contains('tariff-expandable-text')) return;
const expanded=box.classList.toggle('is-expanded');
box.classList.toggle('is-collapsed', !expanded);
toggleBtn.setAttribute('aria-expanded', expanded ? 'true':'false');
toggleBtn.textContent=expanded ? 'Скрыть':'Показать полностью';
});
document.addEventListener('keydown', function (e){
if(e.key==='Escape'){
closeTariffDetails();
}});
}
function setTariffDaysFromHash(days){
days=parseInt(days, 10)||0;
if(days <=0) return false;
const dayInp=document.getElementById('dayInp');
const dayWord=document.getElementById('dayWord');
const chip=Array.from(document.querySelectorAll('#dayChips [data-d]')).find(function (btn){
return String(btn.dataset.d||'')===String(days);
});
if(chip){
chip.click();
return true;
}
if(dayInp){
dayInp.value=String(days);
dayInp.dispatchEvent(new Event('input', { bubbles: true }));
dayInp.dispatchEvent(new Event('change', { bubbles: true }));
}
if(dayWord&&typeof dW==='function'){
dayWord.textContent=dW(days);
}
if(typeof window.filterTariffs==='function'){
window.filterTariffs();
}
return !!dayInp;
}
function openTariffDetailsFromHash(){
if(!window.RedesimTariffHash) return false;
const hashData=typeof window.RedesimTariffHash.getData==='function'
? window.RedesimTariffHash.getData()
: { packageCode: window.RedesimTariffHash.getCode(), days: 0 };
const packageCode=hashData.packageCode;
const days=parseInt(hashData.days||0, 10)||0;
if(!packageCode) return false;
const row=window.RedesimTariffHash.findRow(packageCode);
if(!row) return false;
if(days > 0){
setTariffDaysFromHash(days);
}
if(typeof openCheckoutModalFromRow==='function'&&openCheckoutModalFromRow(row)){
return true;
}
openTariffDetails(row);
return true;
}
setTimeout(openTariffDetailsFromHash, 120);
window.addEventListener('hashchange', function (){
openTariffDetailsFromHash();
});
initProviderColumn();
updateTariffCardCoverageSummaries(document);
syncTariffCardsVisibility();
applyInlineTariffTextCollapse(document);
initTariffDetailsModal();
initCheckoutModalSpecCollapse();
function runKeepingPageScroll(callback){
if(window.RedesimKeepPageScrollPosition&&typeof window.RedesimKeepPageScrollPosition==='function'){
window.RedesimKeepPageScrollPosition(callback);
return;
}
callback();
}
document.addEventListener('click', function (e){
if(e.target.closest('#dayChips .dc') ||
e.target.closest('#gbChips .dc') ||
e.target.closest('.day-adj') ||
e.target.closest('.lf-check') ||
e.target.closest('[data-region-country-option]') ||
e.target.closest('[data-region-country-reset]')
){
setTimeout(function (){
runKeepingPageScroll(function (){
syncTariffCardsVisibility();
refreshActiveProvider();
});
}, 90);
}});
document.addEventListener('change', function (e){
if(e.target&&e.target.matches&&(
e.target.matches('input[name="locFilter"]') ||
e.target.matches('[data-region-country-code]')
)){
setTimeout(function (){
runKeepingPageScroll(function (){
syncTariffCardsVisibility();
refreshActiveProvider();
});
}, 90);
}});
document.addEventListener('input', function (e){
if(e.target&&(e.target.id==='dayInp'||e.target.id==='drawer-dayInp')){
setTimeout(function (){
runKeepingPageScroll(function (){
syncTariffCardsVisibility();
refreshActiveProvider();
});
}, 90);
}});
const observer=new MutationObserver(function (){
syncTariffCardsVisibility();
refreshActiveProvider();
});
observer.observe(provCols, {
subtree: true,
attributes: true,
attributeFilter: ['style', 'class', 'hidden']
});
window.syncTariffCardsVisibility=syncTariffCardsVisibility;
window.getTariffCheckoutDataFromRow=getTariffCheckoutDataFromRow;
window.openCheckoutModalFromRow=openCheckoutModalFromRow;
window.openTariffDetails=openTariffDetails;
window.closeTariffDetails=closeTariffDetails;
})();