1(()=>{'use strict';const cfg=window.NiaPulse;if(!cfg)return;const reduced=matchMedia('(prefers-reduced-motion: reduce)');const el=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!==undefined)n.textContent=text;return n;};const button=(text,fn)=>{const b=el('button','',text);b.type='button';b.addEventListener('click',fn);return b;};const link=(text,url)=>{const a=el('a','',text);a.href=url;return a;};async function request(path,args={}){const url=new URL(cfg.api+path);Object.entries(args).forEach(([k,v])=>url.searchParams.set(k,v));const r=await fetch(url,{cache:'no-store'});const data=await r.json();if(!r.ok)throw Error(data.message||'Ðе ÑдалоÑÑ Ð·Ð°Ð³ÑÑзиÑÑ. ÐовÑоÑиÑе попÑÑкÑ.');return data;}function read(root){return JSON.parse(root.querySelector('.nps-data').textContent);}function rows(articles,kind){const list=el('ol','nps-rows'+(kind==='story'?' nps-timeline':''));for(const a of articles){const li=el('li');const time=el('time','',a.date);time.dateTime=a.datetime;li.append(time,link(a.title,a.url));list.append(li);}return list;} 2function tile(c){const art=el('article','nps-tile nps-color-'+c.color);art.dataset.chain=c.id;const b=button('',()=>{});b.className='nps-tile-toggle';b.setAttribute('aria-expanded','false');b.setAttribute('aria-controls','nps-detail-'+c.id);b.append(el('span','nps-type',c.label),el('strong','nps-title',c.title),el('span','nps-short',c.short));if(c.articles[0]){const a=c.articles[0];b.append(el('span','nps-latest',a.title));const foot=el('span','nps-tile-foot');const time=el('time','',a.date);time.dateTime=a.datetime;foot.append(time,el('span','',c.total+' маÑеÑиалов · РаÑкÑÑÑÑ â'));b.append(foot);}art.append(b);return art;} 3function detail(c,{auto=true,onClose=()=>{}}={}){ 4 const pageSize=4;let offset=0,listRows=[...c.articles],loaded=listRows.length,total=c.total,paused=reduced.matches,busy=false,visible=false,last=0,elapsed=0,position=0,raf=0,destroyed=false,loopDistance=0; 5 const box=el('section','nps-detail nps-color-'+c.color);box.id='nps-detail-'+c.id;box.setAttribute('aria-label',c.title); 6 const head=el('div','nps-detail-heading'),h=el('h3');h.append(link(c.title,c.url));head.append(h,el('span','nps-type',c.label)); 7 const windowEl=el('div','nps-motion-window');let list=rows(listRows,c.kind);windowEl.append(list); 8 const controls=el('div','nps-detail-controls'),note=el('span','nps-detail-note');note.role='status'; 9 const prev=button('Ðовее',()=>turn(-1)),next=button('СÑаÑее',()=>turn(1)),pause=button('ÐаÑза',()=>{paused=!paused;last=0;elapsed=0;sync();}),close=button('СвеÑнÑÑÑ',onClose),all=link('ÐÑе '+total+' маÑеÑиалов â',c.url);all.className='nps-all';controls.append(prev,next);if(auto)controls.append(pause);controls.append(close,all);box.append(head,windowEl,controls,note); 10 function sync(){prev.disabled=offset===0&&windowEl.scrollTop<1;next.disabled=offset+pageSize>=total;pause.disabled=reduced.matches||total<2;pause.textContent=paused?'ÐÑодолжиÑÑ':'ÐаÑза';pause.setAttribute('aria-pressed',String(paused));} 11 function stop(){paused=true;sync();} 12 function paint(){list=rows(listRows,c.kind);windowEl.replaceChildren(list);windowEl.scrollTop=0;position=0;elapsed=0;loopDistance=0;sync();} 13 async function turn(direction){stop();if(busy)return;busy=true;const top=windowEl.getBoundingClientRect().top;const hidden=[...list.children].filter(li=>!li.dataset.repeat&&li.getBoundingClientRect().bottom<=top+1).length;const newOffset=Math.max(0,Math.min(Math.max(0,total-pageSize),offset+hidden+direction*pageSize));try{const data=await request('chain/'+c.id,{offset:newOffset,limit:pageSize});if(destroyed)return;offset=newOffset;listRows=data.articles;loaded=offset+listRows.length;total=data.total;note.textContent='';paint();}catch(e){if(!destroyed)note.textContent=e.message;}finally{busy=false;}} 14 async function append(){if(busy||loaded>=total||destroyed)return;busy=true;try{const data=await request('chain/'+c.id,{offset:loaded,limit:10});if(destroyed)return;total=data.total;loaded+=data.articles.length;listRows.push(...data.articles);for(const li of [...rows(data.articles,c.kind).children])list.append(li);if(!data.articles.length)loaded=total;sync();}catch(e){if(!destroyed){note.textContent=e.message;stop();}}finally{busy=false;}} 15 function prepareLoop(){if(!list.children.length||loaded<total)return;const original=[...list.children].filter(li=>!li.dataset.repeat),copies=[...list.children].filter(li=>li.dataset.repeat);if(!original.length)return;const naturalHeight=original.at(-1).offsetTop+original.at(-1).offsetHeight-original[0].offsetTop,gap=Math.max(0,windowEl.clientHeight+24-naturalHeight);
15copies.forEach((copy,i)=>{const margin=i%original.length===0?gap+'px':'0px';if(copy.style.marginTop!==margin)copy.style.marginTop=margin;});loopDistance=copies.length?copies[0].offsetTop-original[0].offsetTop:0;let extra=copies.length?list.scrollHeight-loopDistance:0,index=copies.length;while(extra<windowEl.clientHeight+40&&index<500){const copy=original[index%original.length].cloneNode(true);copy.dataset.repeat='1';copy.style.marginTop=index%original.length===0?gap+'px':'0px';copy.setAttribute('aria-hidden','true');for(const a of copy.querySelectorAll('a'))a.tabIndex=-1;list.append(copy);if(index===0)loopDistance=copy.offsetTop-original[0].offsetTop;extra=list.scrollHeight-loopDistance;index++;}} 16 function frame(t){if(destroyed)return;const dt=last?Math.min(t-last,100):0;last=t;if(auto&&!paused&&!reduced.matches&&!document.hidden&&visible){elapsed+=dt;prepareLoop();const max=windowEl.scrollHeight-windowEl.clientHeight;if(loaded<total&&max-windowEl.scrollTop<180)append();if(elapsed>1600&&max>0){position=Math.min(max,position+dt*.008);if(loopDistance&&position>=loopDistance)position%=loopDistance;windowEl.scrollTop=position;prev.disabled=offset===0&&position<1;}}raf=requestAnimationFrame(frame);} 17 // Keep moving after opening with a mouse; pausing is an explicit reader control. 18 windowEl.addEventListener('focusin',stop); 19 const obs=new IntersectionObserver(entries=>{visible=entries[0].isIntersecting;last=0;});obs.observe(box); 20 const reducedChange=()=>{if(reduced.matches)stop();sync();};reduced.addEventListener('change',reducedChange); 21 if(auto)raf=requestAnimationFrame(frame);sync(); 22 return {box,stop,destroy(){destroyed=true;cancelAnimationFrame(raf);obs.disconnect();reduced.removeEventListener('change',reducedChange);}}; 23} 24for(const root of document.querySelectorAll('[data-nps-home]')){let data=read(root),active=null,openId=0,offset=0,busy=false;const panels=root.querySelector('.nps-home-panels'),status=root.querySelector('.nps-status'),more=root.querySelector('[data-next]'),first=root.querySelector('[data-first]');function close(focus=false){active?.destroy();active=null;const chosen=panels.querySelector('.nps-open');chosen?.querySelector('.nps-detail')?.remove();panels.classList.remove('nps-has-open');panels.querySelectorAll('.nps-tile').forEach(t=>{t.classList.remove('nps-open');t.querySelector('button').setAttribute('aria-expanded','false');});openId=0;if(focus)chosen?.querySelector('button').focus();}function attach(){panels.querySelectorAll('.nps-tile').forEach(t=>t.querySelector('button').onclick=()=>{const id=Number(t.dataset.chain);if(openId===id){close(true);return;}close();const c=data.items.find(x=>x.id===id);openId=id;t.classList.add('nps-open');panels.classList.add('nps-has-open');t.querySelector('button').setAttribute('aria-expanded','true');active=detail(c,{auto:true,onClose:()=>close(true)});t.append(active.box);});}async function batch(start){if(busy)return;busy=true;more.disabled=true;try{const result=await request('catalog',{home:1,offset:start,snapshot:data.snapshot});close();panels.classList.add('nps-changing');await new Promise(r=>setTimeout(r,reduced.matches?0:220));data=result;offset=start;panels.replaceChildren(...data.items.map(tile));attach();panels.classList.remove('nps-changing');first.disabled=!offset;status.textContent='';}catch(e){status.textContent=e.message;}finally{busy=false;more.disabled=!data.more;}}more.onclick=()=>batch(data.offset);first.onclick=()=>batch(Math.max(0,offset-5));root.querySelector('[data-reset]').onclick=()=>close(true);root.addEventListener('keydown',e=>{if(e.key==='Escape')close(true);});attach();} 25// Up to 50 complete tiles belong to one catalogue page. Native scrolling and 26// page links remain usable without JavaScript; JavaScript only opens the ticker. 27for(const root of document.querySelectorAll('[data-nps-catalog]')){ 28 const data=read(root),grid=root.querySelector('.nps-catalog-grid');let opened=0,active=null; 29 function close(focus=false){const old=grid.querySelector(`[data-chain="${opened}"] button`);active?.destroy();active=null;grid.querySelector('.nps-detail')?.remove();for(const t of grid.querySelectorAll('.nps-tile')){t.classList.remove('nps-open');t.querySelector('button').setAttribute('aria-expanded','false');}opened=0;if(focus)old?.focus();} 30 function place(){if(!active)return;const tiles=[...grid.querySelectorAll('.nps-tile')],selected=tiles.find(t=>Number(t.dataset.chain)===opened);if(!selected)return;active.box.remove();const top=selected.offsetTop,row=tiles.filter(t=>
30Math.abs(t.offsetTop-top)<3);row.at(-1).after(active.box);} 31 for(const t of grid.querySelectorAll('.nps-tile'))t.querySelector('button').onclick=()=>{const id=Number(t.dataset.chain);if(opened===id){close(true);return;}close();const c=data.items.find(x=>x.id===id);if(!c)return;opened=id;t.classList.add('nps-open');t.querySelector('button').setAttribute('aria-expanded','true');active=detail(c,{auto:true,onClose:()=>close(true)});place();}; 32 root.addEventListener('keydown',e=>{if(e.key==='Escape')close(true);}); 33 let timer;window.addEventListener('resize',()=>{clearTimeout(timer);timer=setTimeout(place,120);}); 34} 35for(const root of document.querySelectorAll('[data-nps-related]')){for(const b of root.querySelectorAll('.nps-switches button'))b.onclick=()=>{for(const other of root.querySelectorAll('.nps-switches button')){const selected=other===b;other.setAttribute('aria-expanded',String(selected));root.querySelector('#'+other.getAttribute('aria-controls')).hidden=!selected;}};} 36})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.