PageSourceSearch

https://www.svetkouzel.com/user/documents/adminsvet/cz-schema.js?v=11

js svetkouzel.com collected 2026-09-25 06:26:31 UTC 25,217 bytes, 669 lines download raw bytes

1/* =============================================================
2   sk-schema.js – AI-Ready JSON-LD
3   svetkouzel.com | Apollo/Classic šablona na Shoptetu
4   Umístění FTP: /user/documents/adminsvet/sk-schema.js
5   Vložení v zápatí Shoptetu (za script.js od Turského):
6   <script src="https://www.svetkouzel.com/user/documents/adminsvet/sk-schema.js?v=11" defer></script>
7   ============================================================= */
8
9(function () {
10  'use strict';
11
12  var SCRIPT_ID        = 'sk-product-schema';
13  var SITE_URL         = 'https://www.svetkouzel.com';
14  var ORG_ID           = SITE_URL + '/#organization';
15  var STORE_ID         = SITE_URL + '/#store';
16  var RETURN_POLICY_ID = SITE_URL + '/#returnPolicy';
17
18  /* ── Helpers ─────────────────────────────────────────────── */
19
20  function onReady(fn) {
21    if (document.readyState === 'loading') {
22      document.addEventListener('DOMContentLoaded', fn, { once: true, passive: true });
23    } else {
24      fn();
25    }
26  }
27
28  function isProductDetail() {
29    var b = document.body;
30    return b.classList.contains('type-product') && b.classList.contains('type-detail');
31  }
32
33  function getDL() {
34    try {
35      if (typeof window.getShoptetDataLayer === 'function') {
36        return window.getShoptetDataLayer() || null;
37      }
38    } catch (e) {}
39    return null;
40  }
41
42  function applyGtin(obj, raw) {
43    if (!raw) return;
44    var digits = String(raw).replace(/\D/g, '');
45    if (!digits) return;
46    var len = digits.length;
47    if (len === 8)       obj.gtin8  = digits;
48    else if (len === 12) obj.gtin12 = digits;
49    else if (len === 13) obj.gtin13 = digits;
50    else if (len === 14) obj.gtin14 = digits;
51    else                 obj.gtin   = digits;
52  }
53
54  function getEan() {
55    var el = document.querySelector('.productEan__value')
56           || document.querySelector('[class*="productEan"][class*="value"]')
57           || document.querySelector('[class*="productEan"]');
58    if (!el) return null;
59    var v = el.textContent.trim();
60    return /^\d{8,14}$/.test(v) ? v : null;
61  }
62
63  function getRating() {
64    var starEl = document.querySelector(
65      '.p-detail .star[data-original-title], .p-detail-info .star[data-original-title]'
66    );
67    var ratingValue = null;
68    if (starEl) {
69      var title = starEl.getAttribute('data-original-title') || '';
70      var m = title.match(/(\d+[.,]\d+|\d+)\s*\/\s*5/);
71      if (m) ratingValue = parseFloat(m[1].replace(',', '.'));
72    }
73    var labelEl = document.querySelector('.p-detail .stars-label, .p-detail-info .stars-label');
74    var reviewCount = null;
75    if (labelEl) {
76      var mc = labelEl.textContent.match(/(\d+)/);
77      if (mc) reviewCount = parseInt(mc[1], 10);
78    }
79    if (!ratingValue || !reviewCount || reviewCount < 1) return null;
80    if (ratingValue < 0 || ratingValue > 5) return null;
81    return { ratingValue: ratingValue, reviewCount: reviewCount };
82  }
83
84  function getReviews() {
85    var reviewEls = document.querySelectorAll('[itemprop="review"]');
86    if (!reviewEls || reviewEls.length === 0) return null;
87    var reviews = [];
88    reviewEls.forEach(function (el) {
89      var ratingEl = el.querySelector('[itemprop="ratingValue"]');
90      var authorEl = el.querySelector('[itemprop="name"]');
91      var bodyEl   = el.querySelector('[itemprop="reviewBody"]');
92      var dateEl   = el.querySelector('[data-testid="latestContributionDate"]');
93      var ratingValue = ratingEl ? parseFloat(ratingEl.textContent.trim()) : null;
94      var authorName  = authorEl ? authorEl.textContent.trim() : null;
95      var reviewBody  = bodyEl   ? bodyEl.textContent.replace(/\s+/g, ' ').trim() : null;
96      if (!ratingValue || !authorName) return;
97      if (ratingValue < 1 || ratingValue > 5) return;
98      var review = {
99        '@type': 'Review',
100        'reviewRating': {
101          '@type': 'Rating',
102          'ratingValue': ratingValue,
103          'bestRating': 5,
104          'worstRating': 1
105        },
106        'author': { '@type': 'Person', 'name': authorName }
107      };
108      if (reviewBody) review.reviewBody = reviewBody;
109      if (dateEl) {
110        var d = dateEl.textContent.trim().match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
111        if (d) review.datePublished = d[3] + '-' + String(d[2]).padStart(2,'0') + '-' + String(d[1]).padStart(2,'0');
112      }
113      reviews.push(review);
114    });
115    return reviews.length > 0 ? reviews : null;
116  }
117
118  function getBreadcrumbs() {
119    var items = document.querySelectorAll('[itemtype="https://schema.org/ListItem"]');
120    if (!items || items.length === 0) return null;
121    var list = [];
122    items.forEach(function (el) {
123      var posEl  = el.querySelector('[itemprop="position"]');
124      var nameEl = el.querySelector('[itemprop="name"]');
125      var itemEl = el.querySelector('[itemprop="item"]');
126      var position = posEl ? parseInt(posEl.getAttribute('content') || posEl.textContent) : null;
127      var name     = nameEl ? nameEl.getAttribute('data-title') || nameEl.textContent.trim() : null;
128      var url      = itemEl ? (itemEl.getAttribute('href') || itemEl.getAttribute('content')) : null;
129      if (!position || !name) return;
130      if (url && url.indexOf('http') !== 0) url = window.location.origin + url;
131      if (!url) url = window.location.href.split('?')[0];
132      list.push({
133        '@type': 'ListItem',
134        'position': position,
135        'name': name.replace(/\s+/g, ' ').trim(),
136        'item': url
137      });
138    });
139    if (list.length === 0) return null;
140    return {
141      '@context': 'https://schema.org',
142      '@type': 'BreadcrumbList',
143      'itemListElement': list
144    };
145  }
146
147  function getFaq() {
148    var items = document.querySelectorAll('.js-faq-block .accordion__item, .faq-block .accordion__item');
149    if (!items || items.length === 0) return null;
150    var faqItems = [];
151    items.forEach(function (item) {
152      var qEl = item.querySelector('.accordion__trigger');
153      var aEl = item.querySelector('.accordion__content');
154      if (!qEl || !aEl) return;
155      var question = qEl.textContent.replace(/\s+/g, ' ').trim();
156      var answer   = (aEl.innerText || aEl.textContent).replace(/\s+/g, ' ').trim();
157      if (!question || !answer) return;
158      faqItems.push({
159        '@type': 'Question',
160        'name': question,
161        'acceptedAnswer': { '@type': 'Answer', 'text': answer }
162      });
163    });
164    if (faqItems.length === 0) return null;
165    return {
166      '@context': 'https://schema.org',
167      '@type': 'FAQPage',
168      'mainEntity': faqItems
169    };
170  }
171
172  function getDescription() {
173    var el = document.querySelector(
174      '#description .extended-description, #description .basic-description, ' +
175      '#description .description-inner, #description'
176    );
177    if (!el) return null;
178    var txt = (el.innerText || el.textContent || '').replace(/\s+/g, ' ').trim();
179    return txt ? txt.slice(0, 5000) : null;
180  }
181
182  function getImage() {
183    var og = document.querySelector('meta[property="og:image"]');
184    if (og && og.content) return [og.content];
185    var ip = document.querySelector('meta[itemprop="image"]');
186    if (ip && ip.content) return [ip.content];
187    return null;
188  }
189
190  function getAvailability(dlProduct) {
191    if (dlProduct && dlProduct.codes && dlProduct.codes.length > 0) {
192      var code = dlProduct.codes[0];
193      if (code && code.quantity) {
194        var q = String(code.quantity).toLowerCase();
195        if (q === '0' || q === 'out of stock') return 'https://schema.org/OutOfStock';
196        return 'https://schema.org/InStock';
197      }
198    }
199    var el = document.querySelector('.availability-value, .p-detail-availability, [class*="availability"]');
200    if (el) {
201      var t = el.textContent.toLowerCase();
202      if (/nedostup|vyprod|out of stock/.test(t)) return 'https://schema.org/OutOfStock';
203      if (/objedn\u00E1vce|preorder|p\u0159edobjedn\u00E1vce/.test(t)) return 'https://schema.org/PreOrder';
204    }
205    return 'https://schema.org/InStock';
206  }
207
208  function watchParams(callback) {
209    var SKIP = /^(ean|gtin|k\u00F3d|code|sku)$/i;
210    var done = false;
211    function parse() {
212      if (done) return;
213      var tbody = document.querySelector('table.detail-parameters tbody');
214      if (!tbody) return callback([]);
215      var rows = tbody.querySelectorAll('tr');
216      if (rows.length === 0) return;
217      done = true;
218      var props = [];
219      rows.forEach(function (row) {
220        if (row.classList.contains('productEan')) return;
221        var th = row.querySelector('th');
222        var td = row.querySelector('td');
223        var k = th ? th.textContent.trim() : '';
224        var v = td ? td.textContent.trim() : '';
225        if (!k || !v || SKIP.test(k)) return;
226        props.push({ '@type': 'PropertyValue', 'name': k, 'value': v });
227      });
228      callback(props);
229    }
230    parse();
231    if (done) return;
232    var target = document.querySelector('table.detail-parameters');
233    if (target) {
234      var obs = new MutationObserver(function () { parse(); });
235      obs.observe(target, { childList: true, subtree: true });
236    }
237    setTimeout(function () { if (!done) { done = true; callback([]); } }, 3000);
238  }
239
240  /* ── Organization + WebSite + Store schema ───────────────── */
241
242  function buildOrganization() {
243    return {
244      '@context': 'https://schema.org',
245      '@type': ['OnlineStore', 'LocalBusiness'],
246      '@id': ORG_ID,
247      'name': 'Sv\u011Bt Kouzel',
248      'alternateName': 'Svetkouzel.com',
249      'description': 'Specializovan\u00FD obchod s licencovan\u00FDmi Harry Potter produkty \u2013 d\u00E1rky, sladkosti, h\u016Flky a sb\u011Bratelský merch. Kamenn\u00FD showroom v Praze.',
250      'url': SITE_URL,
251      'logo': {
252        '@type': 'ImageObject',
253        'url': 'https://cdn.myshoptet.com/usr/www.svetkouzel.com/user/logos/logo.png',
254        'width': 200,
255        'height': 60
256      },
257      'image': 'https://cdn.myshoptet.com/usr/www.svetkouzel.com/user/front_images/ogImage/hp.png',
258      'foundingDate': '2022',
259      'legalName': 'FELLICIS s.r.o.',
260      'vatID': 'CZ21058369',
261      'numberOfEmployees': { '@type': 'QuantitativeValue', 'value': 10 },
262      'priceRange': '150\u20135000 K\u010D',
263      // Fakturační adresa
264      'legalAddress': {
265        '@type': 'PostalAddress',
266        'streetAddress': 'Kytlick\u00E1 759/23',
267        'addressLocality': 'Praha 9',
268        'postalCode': '190 00',
269        'addressCountry': 'CZ'
270      },
271      // Sklad / provozovna
272      'address': {
273        '@type': 'PostalAddress',
274        'streetAddress': 'Na Pankr\u00E1ci 1352/2',
275        'addressLocality': 'Praha 4',
276        'postalCode': '140 00',
277        'addressCountry': 'CZ'
278      },
279      'contactPoint': {
280        '@type': 'ContactPoint',
281        'telephone': '+420725338071',
282        'email': '[email protected]',
283        'contactType': 'customer service',
284        'availableLanguage': ['Czech', 'Slovak', 'English'],
285        'hoursAvailable': {
286          '@type': 'OpeningHoursSpecification',
287          'dayOfWeek': ['Monday','Tuesday','Wednesday','Thursday','Friday'],
288          'opens': '09:00',
289          'closes': '17:00'
290        }
291      },
292      'sameAs': [
293        'https://www.facebook.com/svetkouzell',
294        'https://www.instagram.com/svetkouzel/'
295      ],
296      'areaServed': ['CZ','SK','HU','DE','AT','PL','HR','SI','DK'],
297      'currenciesAccepted': 'CZK, EUR',
298      'paymentAccepted': 'Credit Card, Bank Transfer, Cash on Delivery',
299      'hasMerchantReturnPolicy': [
300        {
301          '@type': 'MerchantReturnPolicy',
302          '@id': RETURN_POLICY_ID,
303          'applicableCountry': 'CZ',
304          'returnPolicyCategory': 'https://schema.org/MerchantReturnFiniteReturnWindow',
305          'merchantReturnDays': 14,
306          'returnMethod': 'https://schema.org/ReturnByMail',
307          'returnFees': 'https://schema.org/FreeReturn'
308        },
309        {
310          '@type': 'MerchantReturnPolicy',
311          'applicableCountry': 'SK',
312          'returnPolicyCategory': 'https://schema.org/MerchantReturnFiniteReturnWindow',
313          'merchantReturnDays': 14,
314          'returnMethod': 'https://schema.org/ReturnByMail',
315          'returnFees': 'https://schema.org/FreeReturn'
316        }
317      ]
318    };
319  }
320
321  function buildWebSite() {
322    return {
323      '@context': 'https://schema.org',
324      '@type': 'WebSite',
325      '@id': SITE_URL + '/#website',
326      'name': 'Sv\u011Bt Kouzel',
327      'url': SITE_URL,
328      'description': 'Specializovan\u00FD obchod s licencovan\u00FDmi Harry Potter produkty \u2013 d\u00E1rky, sladkosti, h\u016Flky a sb\u011Bratelský merch. Kamenn\u00FD showroom v Praze.',
329      'inLanguage': 'cs-CZ',
330      'publisher': { '@id': ORG_ID },
331      'potentialAction': {
332        '@type': 'SearchAction',
333        'target': {
334          '@type': 'EntryPoint',
335          'urlTemplate': SITE_URL + '/vyhledavani/?q={search_term_string}'
336        },
337        'query-input': 'required name=search_term_string'
338      }
339    };
340  }
341
342  function buildStore() {
343    return {
344      '@context': 'https://schema.org',
345      '@type': 'Store',
346      '@id': STORE_ID,
347      'name': 'Sv\u011Bt Kouzel \u2013 Prodejna Praha',
348      'url': SITE_URL,
349      'telephone': '+420725338071',
350      'email': '[email protected]',
351      'address': {
352        '@type': 'PostalAddress',
353        'streetAddress': 'Na Pankr\u00E1ci 1352/2',
354        'addressLocality': 'Praha 4',
355        'postalCode': '140 00',
356        'addressCountry': 'CZ'
357      },
358      'geo': {
359        '@type': 'GeoCoordinates',
360        'latitude': 50.0631,
361        'longitude': 14.4289
362      },
363      'openingHoursSpecification': [
364        {
365          '@type': 'OpeningHoursSpecification',
366          'dayOfWeek': ['Monday','Tuesday','Thursday','Friday'],
367          'opens': '14:30',
368          'closes': '18:00'
369        },
370        {
371          '@type': 'OpeningHoursSpecification',
372          'dayOfWeek': ['Saturday','Sunday'],
373          'opens': '15:00',
374          'closes': '18:00'
375        }
376      ],
377      'parentOrganization': { '@id': ORG_ID }
378    };
379  }
380
381  function injectGlobalSchemas() {
382    // 1. Organization — nahradit existující chudou verzi od Shoptetu
383    var replaced = false;
384    document.querySelectorAll('script[type="application/ld+json"]').forEach(function (s) {
385      try {
386        var d = JSON.parse(s.textContent);
387        if (d['@type'] === 'Organization' || d['@type'] === 'OnlineStore') {
388          s.textContent = JSON.stringify(buildOrganization());
389          replaced = true;
390        }
391      } catch (e) {}
392    });
393    if (!replaced) {
394      var orgScript = document.createElement('script');
395      orgScript.type = 'application/ld+json';
396      orgScript.id = 'sk-organization';
397      orgScript.text = JSON.stringify(buildOrganization());
398      (document.head || document.documentElement).appendChild(orgScript);
399    }
400
401    // 2. WebSite — přidat pokud ještě neexistuje
402    var hasWebSite = false;
403    document.querySelectorAll('script[type="application/ld+json"]').forEach(function (s) {
404      try { if (JSON.parse(s.textContent)['@type'] === 'WebSite') hasWebSite = true; } catch (e) {}
405    });
406    if (!hasWebSite) {
407      var wsScript = document.createElement('script');
408      wsScript.type = 'application/ld+json';
409      wsScript.id = 'sk-website';
410      wsScript.text = JSON.stringify(buildWebSite());
411      (document.head || document.documentElement).appendChild(wsScript);
412    }
413
414    // 3. Store — přidat/aktualizovat (fyzická prodejna — jen CZ web)
415    var existingStore = document.getElementById('sk-local-business');
416    if (existingStore) existingStore.parentNode.removeChild(existingStore);
417    var storeScript = document.createElement('script');
418    storeScript.type = 'application/ld+json';
419    storeScript.id = 'sk-local-business';
420    storeScript.text = JSON.stringify(buildStore());
421    (document.head || document.documentElement).appendChild(storeScript);
422  }
423
424  /* ── Product schema inject ───────────────────────────────── */
425
426  function inject(obj) {
427    var old = document.getElementById(SCRIPT_ID);
428    if (old) old.parentNode.removeChild(old);
429    // Odstranit základní Product schema od Shoptetu (bez @id)
430    document.querySelectorAll('script[type="application/ld+json"]').forEach(function (s) {
431      if (s.id === SCRIPT_ID) return;
432      try {
433        var d = JSON.parse(s.textContent);
434        if (d['@type'] === 'Product' && !d['@id']) s.parentNode.removeChild(s);
435      } catch (e) {}
436    });
437    var s = document.createElement('script');
438    s.type = 'application/ld+json';
439    s.id = SCRIPT_ID;
440    s.text = JSON.stringify(obj);
441    (document.head || document.documentElement).appendChild(s);
442  }
443
444  /* ── Hlavní build funkce ─────────────────────────────────── */
445
446  function tryBuild() {
447    if (!isProductDetail()) return false;
448
449    var dl = getDL();
450    if (!dl || !dl.product) return false;
451
452    var p = dl.product;
453    var url = (window.location.href || '').split('#')[0];
454
455    var name = p.name
456      || (document.querySelector('.p-detail .h1, h1') || {}).textContent
457      || null;
458    if (!name) return false;
459    name = name.replace(/\s+/g, ' ').trim();
460
461    var currency = dl.currency || 'CZK';
462    var ean         = getEan();
463    var rating      = getRating();
464    var reviews     = getReviews();
465    var images      = getImage();
466    var description = getDescription();
467    var availability = getAvailability(p);
468    var weightKg    = p.weight ? parseFloat(p.weight) : null;
469    var brandName   = p.manufacturer || null;
470    var sku         = (p.codes && p.codes[0] && p.codes[0].code) ? p.codes[0].code : (p.code || null);
471    var price       = (typeof p.priceWithVat === 'number') ? p.priceWithVat : null;
472    var priceUntil  = (new Date().getFullYear() + 1) + '-12-31';
473
474    var category = null;
475    if (p.currentCategory) {
476      var cats = p.currentCategory.split('|');
477      category = cats[cats.length - 1].trim();
478    }
479
480    var product = {
481      '@context': 'https://schema.org',
482      '@type': 'Product',
483      '@id': url + '#product',
484      'name': name,
485      'url': url
486    };
487
488    if (images)      product.image       = images;
489    if (description) product.description = description;
490    if (sku)         product.sku         = sku;
491    if (brandName)   product.brand       = { '@type': 'Brand', 'name': brandName };
492    if (category)    product.category    = category;
493
494    applyGtin(product, ean);
495
496    if (weightKg && !isNaN(weightKg) && weightKg > 0) {
497      product.weight = {
498        '@type': 'QuantitativeValue',
499        'value': weightKg,
500        'unitText': 'kg',
501        'unitCode': 'KGM'
502      };
503    }
504
505    if (rating) {
506      product.aggregateRating = {
507        '@type': 'AggregateRating',
508        'ratingValue': rating.ratingValue,
509        'reviewCount': rating.reviewCount,
510        'bestRating': 5,
511        'worstRating': 1
512      };
513    }
514
515    if (reviews && reviews.length > 0) product.review = reviews;
516
517    if (price !== null && availability) {
518      product.offers = {
519        '@type': 'Offer',
520        '@id': url + '#offer',
521        'url': url,
522        'price': price,
523        'priceCurrency': currency,
524        'availability': availability,
525        'priceValidUntil': priceUntil,
526        'itemCondition': 'https://schema.org/NewCondition',
527        'seller': {
528          '@type': 'Organization',
529          '@id': ORG_ID,
530          'name': 'Sv\u011Bt Kouzel'
531        },
532        'hasMerchantReturnPolicy': { '@id': RETURN_POLICY_ID },
533        'shippingDetails': {
534          '@type': 'OfferShippingDetails',
535          'shippingRate': {
536            '@type': 'MonetaryAmount',
537            'value': '0',
538            'currency': currency
539          },
540          'shippingDestination': {
541            '@type': 'DefinedRegion',
542            'addressCountry': 'CZ'
543          },
544          'deliveryTime': {
545            '@type': 'ShippingDeliveryTime',
546            'cutoffTime': '14:00:00+01:00',
547            'handlingTime': {
548              '@type': 'QuantitativeValue',
549              'minValue': 0, 'maxValue': 1, 'unitCode': 'DAY'
550            },
551            'transitTime': {
552              '@type': 'QuantitativeValue',
553              'minValue': 1, 'maxValue': 3, 'unitCode': 'DAY'
554            }
555          }
556        }
557      };
558    }
559
560    watchParams(function (additionalProps) {
561      var mapped = { material: null, color: null, extra: [] };
562      additionalProps.forEach(function (prop) {
563        var k = (prop.name || '').toLowerCase();
564        if (/^materi\u00E1l$|^material$/.test(k)) { mapped.material = prop.value; }
565        else if (/^barva$|^color$/.test(k))        { mapped.color    = prop.value; }
566        else                                        { mapped.extra.push(prop); }
567      });
568
569      if (mapped.material) product.material = mapped.material;
570      if (mapped.color)    product.color    = mapped.color;
571      if (mapped.extra.length > 0) product.additionalProperty = mapped.extra;
572
573      inject(product);
574
575      // Breadcrumb
576      var breadcrumb = getBreadcrumbs();
577      if (breadcrumb) {
578        var oldBC = document.getElementById('sk-breadcrumb');
579        if (oldBC) oldBC.parentNode.removeChild(oldBC);
580        var bcScript = document.createElement('script');
581        bcScript.type = 'application/ld+json';
582        bcScript.id = 'sk-breadcrumb';
583        bcScript.text = JSON.stringify(breadcrumb);
584        (document.head || document.documentElement).appendChild(bcScript);
585      }
586
587      // FAQ
588      var faq = getFaq();
589      if (faq) {
590        var oldFaq = document.getElementById('sk-faq');
591        if (oldFaq) oldFaq.parentNode.removeChild(oldFaq);
592        var faqScript = document.createElement('script');
593        faqScript.type = 'application/ld+json';
594        faqScript.id = 'sk-faq';
595        faqScript.text = JSON.stringify(faq);
596        (document.head || document.documentElement).appendChild(faqScript);
597      }
598
599      // Organization + WebSite + Store i na produktové stránce
600      injectGlobalSchemas();
601
602      // Debug (aktivujte přidáním ?schema_debug=1 do URL)
603      if (window.location.search.indexOf('schema_debug=1') !== -1) {
604        console.group('%c\uD83E\uDDD9 Sv\u011Bt Kouzel \u2013 Schema Debug', 'color:#9c27b0;font-size:14px;font-weight:bold');
605        console.log('Produkt:', name);
606        console.log('EAN:', ean || '\u26A0\uFE0F nenalezen');
607        console.log('GTIN pole:', ean ? ('gtin' + ean.length) : '\u2014');
608        console.log('Zna\u010Dka:', brandName || '\u26A0\uFE0F nenalezena');
609        console.log('Hmotnost:', weightKg ? weightKg + ' kg' : '\u26A0\uFE0F nenalezena');
610        console.log('Hodnocen\u00ED:', rating ? rating.ratingValue + '/5 (' + rating.reviewCount + 'x)' : '\u26A0\uFE0F nenalezeno');
611        console.log('Dostupnost:', availability);
612        console.log('Recenze:', reviews ? reviews.length + 'x' : '\u26A0\uFE0F nenalezeny');
613        console.log('Breadcrumb:', breadcrumb ? breadcrumb.itemListElement.length + ' krok\u016F' : '\u26A0\uFE0F nenalezen');
614        console.log('FAQ:', faq ? faq.mainEntity.length + ' ot\u00E1zek' : '\u26AA nen\u00ED na t\u00E9to str\u00E1nce');
615        console.log('Parametry:', additionalProps.length + ' polo\u017Eek');
616        console.log('Pln\u00E9 schema:', JSON.parse(document.getElementById(SCRIPT_ID).textContent));
617        console.groupEnd();
618      }
619    });
620
621    return true;
622  }
623
624  /* ── INIT ────────────────────────────────────────────────── */
625
626  onReady(function () {
627    setTimeout(function () {
628
629      // Na všech stránkách — Organization + WebSite + Store
630      injectGlobalSchemas();
631
632      // FAQ a Breadcrumb na ne-produktových stránkách
633      if (!isProductDetail()) {
634        var faq = getFaq();
635        if (faq) {
636          var oldFaq = document.getElementById('sk-faq');
637          if (oldFaq) oldFaq.parentNode.removeChild(oldFaq);
638          var faqScript = document.createElement('script');
639          faqScript.type = 'application/ld+json';
640          faqScript.id = 'sk-faq';
641          faqScript.text = JSON.stringify(faq);
642          (document.head || document.documentElement).appendChild(faqScript);
643        }
644
645        var breadcrumb = getBreadcrumbs();
646        if (breadcrumb) {
647          var oldBC = document.getElementById('sk-breadcrumb');
648          if (oldBC) oldBC.parentNode.removeChild(oldBC);
649          var bcScript = document.createElement('script');
650          bcScript.type = 'application/ld+json';
651          bcScript.id = 'sk-breadcrumb';
652          bcScript.text = JSON.stringify(breadcrumb);
653          (document.head || document.documentElement).appendChild(bcScript);
654        }
655      }
656
657      // Produktová stránka — Product schema
658      if (isProductDetail()) {
659        if (!tryBuild()) {
660          document.addEventListener('ShoptetDataLayerUpdated', function () {
661            if (!document.getElementById(SCRIPT_ID)) tryBuild();
662          }, { once: true, passive: true });
663        }
664      }
665
666    }, 500);
667  });
668
669})();

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.