PageSourceSearch

https://www.nanolab.cz/user/documents/upload/nl-advisor-v6-simple-20261007/advisor-offers-v6.js

js nanolab.cz collected 2026-10-08 23:02:55 UTC 3,871 bytes, 57 lines download raw bytes

1// Read the price rendered by Shoptet for this customer's current session.
2// No public-price fallback, group whitelist, discount arithmetic or shared cache.
3const clean=value=>String(value??'').replace(/\s+/g,' ').trim();
4const content=(root,property)=>root.querySelector('meta[itemprop="'+property+'"]')?.getAttribute('content');
5export function displayedPrice(element){
6 if(!element)throw Error('customer price missing');
7 const copy=element.cloneNode(true);
8 copy.querySelectorAll('.pr-list-unit').forEach(node=>node.remove());
9 const text=clean(copy.textContent);
10 if(!/^\d[\d ]*(?:[,.]\d{1,2})? Kč$/.test(text))throw Error('customer price ambiguous');
11 const value=Number(text.replace(/ Kč$/,'').replace(/ /g,'').replace(',','.'));
12 if(!Number.isFinite(value)||value<=0)throw Error('customer price invalid');
13 return value;
14}
15export function readOffer(page,code){
16 const detail=page.querySelector('#product-detail-form');
17 if(!detail)throw Error('product detail missing');
18 const offers=[...detail.querySelectorAll('[itemprop="offers"]')];
19 const exact=offers.filter(offer=>(content(offer,'sku')||(offers.length===1?content(detail,'sku'):null))===code);
20 if(exact.length!==1)throw Error('exact SKU offer missing or ambiguous');
21 const offer=exact[0];
22 if(content(offer,'priceCurrency')!=='CZK')throw Error('unsupported currency');
23 const inStock=offer.querySelector('[itemprop="availability"]')?.getAttribute('href')?.endsWith('/InStock');
24
25 const rows=[...page.querySelectorAll('[data-testid="productVariant"]')];
26 let source=detail,priceElement,variant="",variantImage="";
27 if(rows.length){
28  const exactRows=rows.filter(row=>{
29   const label=row.querySelector('[data-testid="productVariantName"]')?.parentElement;
30   const text=label?[...label.childNodes].filter(node=>node.nodeType===3).map(node=>node.textContent).join(' '):'';
31   return clean(text).replace(/^\|\s*/,'')===code;
32  });
33  if(exactRows.length!==1)throw Error('exact variant row missing or ambiguous');
34  variant=clean(exactRows[0].querySelector('[data-testid="productVariantName"]')?.textContent);
35  variantImage=exactRows[0].querySelector('img')?.closest('a.lightbox')?.getAttribute('href')||exactRows[0].querySelector('img')?.getAttribute('data-src')||exactRows[0].querySelector('img')?.getAttribute('src')||'';
36  source=exactRows[0].querySelector('form[action="/action/Cart/addCartItem/"]');
37  priceElement=exactRows[0].querySelector('[data-testid="productVariantPrice"]');
38 }else{
39  if(offers.length!==1)throw Error('variant prices missing');
40  priceElement=detail.querySelector('[data-testid="productCardPrice"]');
41 }
42 const price=displayedPrice(priceElement);
43 const priceId=source?.querySelector('input[name="priceId"]')?.value;
44 const productId=detail.querySelector('input[name="productId"]')?.value||'';
45 const purchasable=!!inStock&&!!priceId&&/^\d+$/.test(priceId)&&/^\d+$/.test(productId)&&!source?.querySelector('button[type="submit"][disabled]');
46 const name=clean(page.querySelector('h1')?.textContent);
47 const imageSource=content(offer,'image')||variantImage||content(detail,'image')||content(page,'image');
48 if(!name||!imageSource||!/^https:\/\//.test(imageSource))throw Error('product metadata unavailable');
49 return {code,name,variant,imageSource,price,currency:'CZK',available:purchasable,priceId,productId,language:detail.querySelector('input[name="language"]')?.value||'cs'};
50}
51export async function fetchOffer(product){
52 const url=new URL(product.url,location.href);
53 if(url.origin!==location.origin)throw Error('product origin mismatch');
54 const response=await fetch(url.href,{credentials:'same-origin',cache:'no-store',signal:AbortSignal.timeout(12000)});
55 if(!response.ok||new URL(response.url||url.href).origin!==location.origin)throw Error('product unavailable');
56 return readOffer(new DOMParser().parseFromString(await response.text(),'text/html'),product.code);
57}

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.