1import queryString from 'query-string';import{createBrowserHistory,createPath,parsePath}from 'history';window.bDigital||(window.bDigital={});export const getCore=async()=>{return await get('bDigital.core')} 2export class Events{#selector;constructor(el,ns){this.el=document;if(el){if(el==='window'){this.el=window}else if(typeof el=='string'){this.#selector=el}else{this.el=el}} 3this.ns=ns;this.list={};this.opts={bubbles:!0}} 4element(val){this.el=val;return this} 5namespace(val){this.ns=val;return this} 6options(val,replace=!1){this.opts=(replace)?val:{...this.opts,...val};return this} 7get(event){return((this.ns)?this.ns+':':'')+event} 8trigger(name,data){return this.el.dispatchEvent(new CustomEvent(this.get(name),{...this.opts,...{detail:data||{}}}))} 9on(event,cb,opts){const callback=(this.#selector)?(e)=>{if(e.target){const el=e.target.closest(this.#selector);el&&cb.call(el,e)}}:cb;this.el.addEventListener(this.get(event),callback,opts);this.list[event]||(this.list[event]=[]);this.list[event].push(callback);return this} 10off(event,opts){const callbacks=this.list[event];if(callbacks){callbacks.forEach(callback=>{this.el.removeEventListener(this.get(event),callback,opts)});delete this.list[event]} 11return this} 12clear(opts){for(const event of Object.keys(this.list)){this.off(event,opts)} 13return this} 14init={instance:this,async scroll(throttle=10){const debounce=await importElement('debounce');const el=(this.instance.el===document)?window:this.instance.el;const get={get top(){return el.scrollY||el.scrollTop||0},get height(){return(el===window)?document.body.scrollHeight:el.scrollHeight},get scroll(){return get.top+(el.clientHeight||el.innerHeight||0)}};let scrollUp=get.top;let scrollDown;el.addEventListener('scroll',debounce(e=>{const detail={event:e,data:get};const top=get.top;const isDown=top>scrollUp;const sameDirection=(scrollDown==isDown);this.instance.trigger('scrolling',detail);if(isDown){sameDirection||this.instance.trigger('scroll:down',detail);this.instance.trigger('scrolling:down',detail)}else{sameDirection||this.instance.trigger('scroll:up',detail);this.instance.trigger('scrolling:up',detail)} 15scrollDown=isDown;scrollUp=top;if(top===0){this.instance.trigger('scroll:top',detail)}else if(get.scroll>=get.height){delay(10).then(()=>this.instance.trigger('scroll:bottom',detail))}},throttle))},hash(){window.addEventListener('hashchange',e=>{this.instance.trigger('hash:change',e)})},page(){document.addEventListener('visibilitychange',e=>{const type=document.hidden?'hidden':'visible';this.instance.trigger('page:'+type,e)})}}} 16export class Observer{constructor(target,callback,opts,instance){this.target=target||document;this.instance=instance||this.default;this.callback=callback||(()=>{});this.opts=opts||{characterDataOldValue:!0,subtree:!0,childList:!0,attributes:!1,characterData:!0,}} 17get default(){return new MutationObserver(mutations=>{mutations.forEach(m=>{const oldValue=m.oldValue;const newValue=m.target.textContent;if(oldValue!==newValue){if(m.addedNodes.length){this.callback.call(this,[...m.addedNodes],'add')} 18if(m.removedNodes.length){this.callback.call(this,[...m.removedNodes],'remove')}}})})} 19observe(opts){this.instance||(this.instance=this.default);this.instance.observe(this.target,{...this.opts,...(opts||{})})} 20disconnect(){this.instance&&this.instance.disconnect()} 21destroy(){this.disconnect();this.instance=null}} 22export class DomObserver{constructor(target,{observer,on}={}){this.target=target||document;this.observer=observer;this.on=on||new this.On(this);this.callbacks={}} 23init(){if(!this.observer){this.observer=new Observer(this.target,(elements,type)=>{const cbList=this.callbacks[type];if(cbList){const mProp='__mutated';Object.entries(cbList).forEach(data=>{const[selector,callbacks]=data;if(callbacks&&callbacks.length){const nodes=[];elements.forEach(node=>{if(node.nodeType==Node.ELEMENT_NODE){if(node.matches(selector)){if(!node[mProp]||type=='remove'){node[mProp]=!0;nodes.push(node)}}else{node.querySelectorAll(selector).forEach(child=>{if(!child[mProp]||type=='remove'){child[mProp]=!0;nodes.push(child)}})}}});if(nodes.length){callbacks.forEach(data=>
23{if(data.each){nodes.forEach(node=>{this.trigger.one(data.cb,node)})}else{this.trigger.many(data.cb,nodes)}})}}})}})} 24this.observer.observe()} 25On=class{constructor(parent){this.parent=parent} 26event(type,selector,callback,batch=!1){if(selector&&callback){const that=this.parent;that.init();that.callbacks[type]||(that.callbacks[type]={});if(batch){if(type=='add'){const nodes=that.target.querySelectorAll(selector);nodes.length&&that.trigger.many(callback,Array.from(nodes),{initial:!0,})} 27that.callbacks[type][selector]=(that.callbacks[type][selector]||[]).concat({cb:callback,})}else{if(type=='add'){that.target.querySelectorAll(selector).forEach(node=>{that.trigger.one(callback,node,{initial:!0,direct:!0,})})} 28that.callbacks[type][selector]=(that.callbacks[type][selector]||[]).concat({each:!0,cb:callback,})}}} 29add(selector,callback){return this.event('add',selector,callback)} 30addBatch(selector,callback){return this.event('add',selector,callback,!0)} 31remove(selector,callback){return this.event('remove',selector,callback)} 32removeBatch(selector,callback){return this.event('remove',selector,callback,!0)}} 33clear(){this.callbacks={};if(this.observer){this.observer.destroy();this.observer=null}} 34trigger={params(opts){return{...{initial:!1,old:null,},...(opts||{})}},one(func,node,opts){func.call(node,node,this.params(opts))},many(func,nodes,opts){func.call(this,nodes,this.params(opts))},}} 35export class SelectorObserver{constructor(selector,instance){this.selector=selector;this.observer=instance||observer} 36on(type,cb){this.observer.on[type](this.selector,cb);return this}} 37export class Visibility{constructor(target,opts={}){this.callbacks={};this.instance=new IntersectionObserver(entries=>{entries.forEach(e=>{const el=e.target;const type=(e.isIntersecting)?'visible':'invisible';const ns='__visibility_';const cur=ns+'current';const prop=ns+type;const visible=(type=='visible');if(el[cur]!=type){this.#trigger(type,el,e)} 38delay(0).then(()=>el[cur]=type);if(!el[prop]){if(visible||el[ns+'visible']){this.#trigger(type+'Once',el,e);delay(10).then(()=>el[prop]=!0)}}})},opts);if(typeof target=='string'){observer.on.add(target,el=>{this.instance.observe(el)});observer.on.remove(target,el=>{this.instance.unobserve(el)})}else{this.instance.observe(target)}} 39on(type,callback){this.callbacks[type]||(this.callbacks[type]=[]);this.callbacks[type].push(callback);return this} 40#trigger(type,el,e){(this.callbacks[type]||[]).forEach(cb=>cb.call(el,el,e))} 41setEvents(list=['visible','invisible','visibleOnce','invisibleOnce']){list.forEach(event=>{this.on(event,el=>{delay(100).then(()=>{el.dispatchEvent(new CustomEvent('visibility:'+event,{bubbles:!0,details:{el:el}}))})})})}} 42export class Obj{constructor(data,separator){this.data=data||{};this.separator=separator||'.'} 43get(route,def){return(route)?route.split(this.separator).reduce((o,p)=>o?o[p]:def,this.data):this.data} 44set(route,value){route.split(this.separator).reduce((o,p,i)=>o[p]=route.split(this.separator).length===++i?value:o[p]||{},this.data);return this} 45add(data){this.data={...this.data,...(data||{})};return this} 46mount(data){this.data=data||{};return this} 47clear(){return this.mount()} 48get length(){return Object.keys(this.data).length}} 49export class Data{constructor(el){this.obj=new Obj(null,'-');el&&this.from(el)} 50from(el){for(const[key,val]of Object.entries(el.dataset)){const name=key.replace(/[A-Z]+(?![a-z])|[A-Z]/g,($,ofs)=>(ofs?"-":"")+$.toLowerCase());this.obj.set(name,val)}} 51get(route,def){return this.obj.get(route,def)} 52add(data){this.obj.add(data)} 53clear(){this.obj.clear()} 54get values(){return this.obj.data}} 55export const delay=ms=>{return new Promise(r=>setTimeout(r,ms))} 56export async function get(name,{target=window,timeout=50,attempts=100,defaultVal}={}){get.promises||(get.promises={});const obj=target?new Obj(target):null;return(obj)?obj.get(name)||(get.promises[name]??=new Promise(resolve=>{let count=0;const interval=setInterval(()=>{const result=obj.get(name);const done=count>=attempts;result&&resolve(result);if(result||done){clearInterval(interval);result||resolve(defaultVal)}else{count++}},timeout)})):defaultVal} 57export async function importElement(module,{name='default',error=!1}={}){let imported={};try{imported=await import(module)}catch(e){if(error){throw e}} 58return imported[name]} 59export class Agent{#mobile;get mobile(){if(!this.#mobile){this.#mobile=window.matchMedia('(max-width: 768px)').matches&&((window.matchMedia("(pointer: coarse)").matches)||('ontouchstart' in window)||(navigator.maxTouchPoints>0)||(navigator.msMaxTouchPoints>0))} 60return this.#mobile} 61get desktop(){return!this.mobile}} 62export const wretchInit=async(url,opts={})=>{await get('wretch');return wretch(url,opts)} 63export const wretch=(url,opts={})=>{return window.wretch(url,opts).headers({'X-Requested-With':'XMLHttpRequest','X-Fetch-Request':!0,'X-Prefer-Json':!0,}).errorType('json')} 64export const lang=(key)=>{let res='';if(key){if(window.LANG){const lang=window.LANG[key];if(lang){res=lang}} 65res||(res=key[0].toUpperCase()+key.slice(1))} 66return res};export class Url{constructor(url,{q={},hash=null}={}){let query={};const parseQuery=(obj,keys=[])=>{if(obj){for(const[key,value]of Object.entries(obj)){if(typeof value=='object'){parseQuery(value,[...keys,...[key]])}
66else{const keysCopy=[...keys];const first=keysCopy.shift();const k=(typeof first!='undefined')?first+keysCopy.map(x=>'['+x+']').join('')+'['+key+']':key;query[k]=value}}}};parseQuery(q);this.url=url||window.location.href;this.q=query;this.hash=hash} 67static from(url,{q={},hash=null}={}){const data=queryString.parseUrl(url||window.location.href,{parseFragmentIdentifier:!0});hash??=data.fragmentIdentifier;return new Url(data.url,{q:{...data.query,...q},hash:hash,})} 68get(opts={}){return queryString.stringifyUrl({url:this.url,query:(opts.noQuery)?null:this.q,fragmentIdentifier:this.hash,skipNull:!0,skipEmptyString:!0,arrayFormat:'index',})} 69absolute(opts){return new URL(this.get(opts),document.baseURI).href} 70relative(opts){let res=null;const current=this.get(opts);if(current.indexOf('//')!==-1){const url=new URL(current);res=url.toString().substring(url.origin.length)}else{res=current} 71return res} 72canonical(opts){return this.get({...opts,...{noQuery:!0}})}} 73export class History{static callbacks={};static #popstate=!1;constructor({el,callback}={}){this.token=new Token();get('bDigital.core.pjax').then(v=>this.pjax=v);this.engine=createBrowserHistory((el)?{window:el}:{});callback&&(History.callbacks[this.token.value]=callback);if(!History.#popstate){History.#popstate=!0;
73window.addEventListener('popstate',e=>{if(e.state&&e.state.usr){const data=e.state.usr||{};const token=data._token;if(token){delete data._token;for(const[tk,cb]of Object.entries(History.callbacks)){if(tk===token){const propagate=cb({e,data});propagate===!1&&e.stopImmediatePropagation()}}}}})} 74this.unlisten=this.engine.listen(({location})=>{this.pjax&&this.pjax.pushState(createPath(location),!0)})} 75get current(){return(this.pjax)?parsePath(new Url(this.pjax.current).relative()):this.engine.location} 76append({path,q,hash,action='push'}={},state={}){const cur=this.current;if(path){cur.pathname=[...cur.pathname.split('/'),...path.split('/')].join('/').replace(/\/{2,}/g,'/')} 77if(q){if(typeof q=='string'){q=queryString.parse(q)} 78const query={...queryString.parse(cur.search),...q};const search=queryString.stringify(query,{skipNull:!0,skipEmptyString:!0,});cur.search=(search)?'?'+search:''} 79if(typeof hash!='undefined'){cur.hash=(hash)?(hash.startsWith('#')?hash:'#'+hash):''} 80this[action](createPath(cur),state)} 81push(url,state){this.proceed({url:url,action:'push',state:state})} 82replace(url,state){this.proceed({url:url,action:'replace',state:state})} 83proceed({url,action='push',state}={}){state||(state={});state._token=this.token.value;const urlStr=(url instanceof Url)?url.get():(url||document.location.href);this.engine[(action=='push'&&createPath(this.current)===urlStr)?'replace':action](urlStr,state)} 84go(...args){this.engine.go(...args)} 85back(){this.engine.back()} 86forward(){this.engine.forward()} 87destroy(){this.unlisten();delete History.callbacks[this.token.value]}} 88export class Uid{static #value=null;constructor(opts={}){this.opts={...{cookie:'bdigital_uid',expire:365*24*60*60,timeout:3000,server:'https://services.bionika-digital.ru/uid/',},...opts};this.expired=!1} 89async get(){if(this.grab()===null){if(!this.load()||this.expired){const value=await this.request()||new Token().value;if(value){this.set(value);this.save()}}} 90return this.grab()} 91load(){let loaded=!1;const parts=('; '+document.cookie).split('; '+this.opts.cookie+'=');const data=(parts.length==2)?parts.pop().split(';').shift():null;if(data){const json=JSON.parse(data)||{};if(json.value){loaded=!0;this.set(json.value)} 92if(json.expire){const d=new Date();if(d.getTime()>json.expire){this.expired=!0}}} 93return loaded} 94async request(){let res=null;try{const response=await fetch(this.opts.server+'?host='+location.protocol+'//'+location.hostname,{credentials:'include',signal:AbortSignal.timeout(this.opts.timeout),});res=await response.text()}catch(ex){} 95return res} 96set(val){Uid.#value=val} 97grab(){return Uid.#value} 98save(){const value=this.grab();if(value){const d=new Date();const data=JSON.stringify({value:value,expire:d.getTime()+this.opts.expire*1000});d.setTime(d.getTime()+(700*24*60*60*1000));document.cookie=this.opts.cookie+'='+data+';expires='+d.toUTCString()+';path=/;domain='+location.hostname.match(/[^\.]*\.[^.]*$/)[0]+';'}} 99static clear(){this.#value=null}};export class User{constructor(data={}){for(const[key,value]of Object.entries({...(window.bDigital.user||{}),...data})){this[key]=value}}} 100export class Err{static from(err){err??=new Error('Error');if(err.message){try{const data=JSON.parse(err.message)||{};const message=data.title||data.message||data.header||data.data;message&&(err.message=message)}catch(ex){if(err.message.match(/\n/)){const message=err.message.match(/\[message\] => (.+)\n/m);const name=err.message.match(/\[class\] => (.+)\n/m);message&&message[1]&&(err.message=message[1]);
100name&&name[1]&&(err.name=name[1])}}} 101return err}} 102export class Response{constructor(obj={}){const isError=obj instanceof Error;this.status=(obj.status||((obj.success)?'success':((obj.error)?'error':null)));this.error=(isError||this.status=='error')?Err.from(obj):null;this.data=(this.error)?{...this.error}:((obj.data&&typeof obj.data=='object')?obj.data:(isError?{}:obj))}} 103export class Timer{value;initial;constructor({from=0,increment=1,period=1000,to,callback,ontick}={}){this.set(from);this.increment=increment;this.period=period;this.to=(typeof to=='undefined')?null:+to;this.completed=!1;this.interval=null;this.timeout=null;this.callback=callback;this.ontick=ontick;this.destroyed=!1;this.paused=!1} 104get time(){return new Date(this.value*1000).toISOString().slice(11,19)} 105set(val){this.initial=val;this.value=val} 106run(from,to){if(typeof from!='undefined'){this.set(from)} 107if(typeof to!='undefined'){this.to=to} 108this.stop();this.start();return this} 109start(){if(!this.destroyed){this.paused||this.reset();this.paused=!1;this.setCompleted(!1);if(typeof this.period=='object'&&this.period.length){const tick=()=>{this.timeout=setTimeout(()=>{this.tick.call(this);this.completed||tick()},Math.floor(Math.random()*(this.period[1]-this.period[0]+1))+this.period[0])};tick()}else{this.interval=setInterval(()=>{this.tick.call(this)},this.period)}} 110return this} 111pause(){this.clear();this.paused=!0} 112stop(){this.clear();this.reset()} 113toggle(v){if(v){this.start()}else{this.pause()}} 114complete(){this.setCompleted(!0);this.clear()} 115reset(){this.value=this.initial} 116tick(){this.value+=this.increment;this.ontick&&this.ontick.call(this,this);if(this.to!==null&&this.value==this.to){this.complete()}} 117clear(){if(this.interval){clearInterval(this.interval);this.interval=null} 118if(this.timeout){clearTimeout(this.timeout);this.timeout=null}} 119setCompleted(val=!0){this.completed=val;val&&this.callback&&this.callback.call(this,this)} 120destroy(){this.destroyed=!0;this.stop()}} 121export class Scripts{static #loaded={};static async load(scripts,callback){const loader=await importElement('load-script');if(typeof scripts!='object'){scripts=[scripts]} 122const promises=[];scripts.forEach(src=>{promises.push(new Promise((rs,rj)=>{let load=this.#loaded[src];if(!load){load=loader(src);this.#loaded[src]=load} 123return load.then(rs).catch(rj)}))});return Promise.all(promises).then((...args)=>{callback&&callback.apply(this,...args)})} 124static execute(el){const scripts=el.querySelectorAll('script');Array.from(scripts).forEach((script)=>{const cloned=document.createElement('script');Array.from(script.attributes).forEach((attribute)=>{cloned.setAttribute(attribute.name,attribute.value)});cloned.text=script.text;script.parentNode.replaceChild(cloned,script)})}} 125export class Page{#type;#sub;constructor({type,sub}={}){this.#type=type;this.#sub=sub;this.timer=new Timer().run();events.on('page:hidden',()=>{this.timer.pause()});events.on('page:visible',()=>{this.timer.start()});events.on('page:unload',()=>{delay(100).then(()=>{this.refresh()})})} 126get type(){return this.#type||bDigital.counters.data.group||(document.querySelector('meta[name=subject]')||{}).content} 127get sub(){return this.#sub||bDigital.counters.data.rubric||(document.querySelector('meta[name=subroubric]')||{}).content} 128get time(){return this.timer.value} 129get data(){return bDigital.counters.data} 130refresh(){this.#type=null;this.#sub=null;this.timer.reset()}} 131export class Message{constructor(data='',params={},manual=!1){this.data=data;this.params={...{type:'success',layout:'topRight',timeout:3000,theme:'semanticui',},...params};manual||this.show()} 132async show(params={}){const Instance=await get('Noty');if(Instance){new Instance({...this.params,...params,...((typeof this.data=='object')?this.data:{text:this.data})}).show()}}} 133export class Token{constructor({size,characters,prefix}={}){this.size=size||32;this.characters=characters||'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';this.prefix=prefix||'';this.generated=null} 134get value(){let res=this.generated;if(!res){res=this.prefix;const len=this.characters.length;for(let i=0;i<this.size;i++){res+=this.characters.charAt(Math.floor(Math.random()*len))} 135this.generated=res} 136return res} 137toString(){return this.value}} 138export class RemoteHtml{constructor(el,{success,callback,error,before,after}={}){this.el=el;this.success=success;this.callback=callback;this.error=error;this.before=before;this.after=after} 139async load(url){return new Promise(r=>{events.trigger('alpine:stop');wretchInit(url).then(wretch=>{this.before&&this.before();const request=wretch.headers({'X-Prefer-Text':!0,'X-Modal':!0,}).get().text(html=>{this.el.innerHTML=html;executeScripts(this.el);delay(100).then(v=>{head.ready(()=>{bDigital.core.trigger.ready(this.el);this.success&&this.success()});r()})}).then(()=>this.after&&this.after()).catch(async err=>{if(this.error){let msg;try{const json=JSON.parse(err.message);msg=json.title||json.data}catch(e){const regex=/\[message\] \=\> (.+)/g;msg=regex.exec(err.message||'')[1]} 140this.error(msg,err)}});this.callback&&this.callback(request)})})}} 141export class Device{static Lib;construct(ua=null){this.ua=ua} 142async lib(){if(!Device.Lib){Device.Lib=await importElement('https://cdn.jsdelivr.net/npm/[email protected]/+esm',{name:'UAParser'})} 143return Device.Lib} 144get parser(){return(async()=>{const Lib=await this.lib();return new Lib(this.ua)})()} 145async info(){const screen={};['availHeight','availWidth','colorDepth','height','isExtended','pixelDepth','width','left','top'].forEach(k=>
145{screen[k]=window.screen[k]});const orientation=window.screen.orientation;screen.orientation={type:orientation.type,angle:orientation.angle,};const nav={};['language','languages','maxTouchPoints','pdfViewerEnabled','buildID','cookieEnabled','doNotTrack','globalPrivacyControl','hardwareConcurrency','onLine','webdriver'].forEach(k=>{nav[k]=window.navigator[k]});return{...{screen:screen,navigator:nav,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,},...(await this.parser).getResult(),}}} 146export const observer=new DomObserver();export const events=new Events(document,'bdigital');export const agent=new Agent();export const user=new User();export const page=new Page();export const history=new History()
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.