1/* 2 * Dynamický banner aktuálnà akce / workshopu. 3 * Zdroj pravdy pro inzerci napÅÃÄ webem â Äte živá data z vibecoding.cz API. 4 * 5 * PoužitÃ: vlož kontejner s id="vibecoding-promo" a data-atributy, 6 * pak naÄti tento skript: 7 * 8 * <div class="posts"> 9 * <div id="vibecoding-promo" data-utm-campaign="event-promo" style="display:none;"></div> 10 * </div> 11 * <script src="/js/promo-banner.js" defer></script> 12 * 13 * data-utm-campaign â hodnota utm_campaign v odkazu (default: "event-promo") 14 */ 15(function () { 16 var container = document.getElementById('vibecoding-promo'); 17 if (!container) return; 18 19 var campaign = container.getAttribute('data-utm-campaign') || 'event-promo'; 20 // Plocha pro promo analytiku (homepage | article | other). Banner bývá v postech â 'article'. 21 var surface = container.getAttribute('data-surface') || 'article'; 22 var months = ['ledna', 'února', 'bÅezna', 'dubna', 'kvÄtna', 'Äervna', 23 'Äervence', 'srpna', 'záÅÃ', 'ÅÃjna', 'listopadu', 'prosince']; 24 25 fetch('https://www.vibecoding.cz/api/active-promotion') 26 .then(function (r) { return r.json(); }) 27 .then(function (e) { 28 if (!e || !e.slug) return; 29 30 // Data z API se vkládajà do innerHTML â escapuj vÅ¡e interpolované. 31 function esc(s) { 32 return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { 33 return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; 34 }); 35 } 36 37 // Reklama / externà promo slot (isAd): nemá reálný termÃn ani mÄsto, 38 // mÃsto âNaÅ¡e akce" má vlastnà štÃtek a CTA âVÃce". Datum se nezobrazuje. 39 var isAd = !!e.isAd; 40 41 var dateText = ''; 42 if (!isAd && e.date) { 43 var d = e.date.split('-'); 44 var df = parseInt(d[2]) + '. ' + months[parseInt(d[1]) - 1] + ' ' + d[0]; 45 var city = e.city || (e.location ? e.location.split(',').slice(-1)[0].trim() : ''); 46 dateText = df + (city ? ' · ' + city : '') + 47 (e.dateVariants && e.dateVariants.length > 1 ? ' · a dalšà termÃny' : ''); 48 } 49 // Klik vede pÅes náš promo redirect (zmÄÅà klik podle site/surface + pÅipojà UTM). 50 // clickUrl nemá query string â parametry pÅidáváme pÅes URL API, ne lepenÃm '&'. 51 // Fallback na pÅÃmý odkaz na akci, kdyby clickUrl chybÄlo (staršà cache odpovÄdi). 52 var url; 53 if (e.clickUrl) { 54 try { 55 var cu = new URL(e.clickUrl); 56 cu.searchParams.set('site', 'marigold'); 57 cu.searchParams.set('surface', surface); 58 url = cu.toString(); 59 } catch (_) { url = e.clickUrl; } 60 } else { 61 url = 'https://www.vibecoding.cz/akce/' + encodeURIComponent(e.slug).replace(/%2F/gi, '/') + 62 '/?utm_source=marigold&utm_medium=web&utm_campaign=' + encodeURIComponent(campaign); 63 } 64 65 if (e.isPaid) { 66 // Tmavý workshop banner 67 var earlyHtml = ''; 68 if (e.earlyBirdPrice && e.earlyBirdDeadline) { 69 var now = new Date(); 70 var deadline = new Date(e.earlyBirdDeadline + 'T23:59:59'); 71 if (now <= deadline) { 72 var ed = e.earlyBirdDeadline.split('-'); 73 earlyHtml = '<div class="workshop-banner-early">Early bird â do ' + 74 parseInt(ed[2]) + '. ' + months[parseInt(ed[1]) - 1] + '.</div>'; 75 } 76 } 77 container.innerHTML = 78 '<a href="' + url + '" class="workshop-banner-link">' + 79 '<div class="workshop-banner">' + 80 '<div class="workshop-banner-content">' + 81 '<div class="workshop-banner-header-row">' + 82 '<span class="workshop-banner-badge">' + (e.isPaid ? 'Workshop' : 'Akce') + '</span>' + 83 '<span class="workshop-banner-date">' + esc(dateText) + '</span>' + 84 '</div>' + 85 '<div class="workshop-banner-title">' + esc(e.title) + '</div>' + 86 (e.bannerDescription ? '<div class="workshop-banner-desc">' + esc(e.bannerDescription) + '</div>' : '') + 87 earlyHtml + 88 '</div>' + 89 '<div class="workshop-banner-cta">' + 90 '<span class="workshop-banner-btn">Detaily →</span>' + 91 '</div>' + 92 '</div>' + 93 '</a>'; 94 } else { 95 // Kompaktnà "NaÅ¡e akce" banner 96 container.innerHTML = 97 '<a href="' + url + '" class="promo-card-compact-link">' + 98 '<div class="promo-card-compact">' + 99 '<div class="promo-left">' + 100 '<span class="promo-badge-sm">' + (isAd ? esc(e.badgeLabel || 'Tip') : 'NaÅ¡e akce') + '</span>' + 101 '<span class="promo-title-sm">' + esc(e.title) + '</span>' + 102 (dateText ? '<span class="promo-meta">' + esc(dateText) + '</span>' : '') + 103 (e.bannerDescription ? '<span class="promo-meta">' + esc(e.bannerDescription) + '</span>' : '') + 104 '</div>' + 105 '<div class="promo-right">' + 106 '<span class="promo-cta-btn">' + (isAd ? 'VÃce' : 'Detaily') + ' →</span>' + 107 '</div>' + 108 '</div>' + 109 '</a>'; 110 } 111 container.style.display = ''; 112 }) 113 .catch(function () {}); 114})();
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.