1/* ============================================================ 2 Ndlala Arena â data-driven display 3 Pulls prices + package details from /api/public/config and updates: 4 - window.NDLALA.TIERS prices (seat picker totals/legend) 5 - [data-price="<tier>"] elements (tier price) 6 - [data-price-from] elements (lowest price) 7 - [data-includes="<tier>"] lists (package "what's included") 8 Static HTML holds fallbacks, so the site works if this fails. 9 Exposes window.NDLALA_CONFIG_READY (a promise) for the seat picker to await. 10 Load in <head>, after site.js. 11 ============================================================ */ 12(function () { 13 function rands(cents) { var n = Math.round((cents || 0) / 100); return 'R' + String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ' '); } 14 15 // What a seat COSTS TODAY. The server publishes both the list price and the 16 // early bird price per tier and decides the discount on its own clock, so the 17 // page never works one out for itself. A browser with a wrong clock would 18 // otherwise advertise a price /api/reserve then refuses to honour. 19 function nowCents(t) { 20 return (t && t.early_price_cents != null) ? t.early_price_cents : (t ? t.price_cents : 0); 21 } 22 23 function updateTiers(cfg) { 24 if (!cfg || !cfg.tiers) return; 25 if (window.NDLALA && window.NDLALA.TIERS) { 26 // The seat picker, the cart and the checkout all total from this one 27 // number, so it has to be the price that will actually be charged. Leaving 28 // it at list would show a total that shrinks at the reserve step. 29 cfg.tiers.forEach(function (t) { if (window.NDLALA.TIERS[t.key]) window.NDLALA.TIERS[t.key].price = Math.round(nowCents(t) / 100); }); 30 } 31 window.NDLALA_EARLY = (cfg && cfg.event && cfg.event.earlyPct > 0) 32 ? { pct: cfg.event.earlyPct, until: cfg.event.earlyUntil } : null; 33 } 34 function applyDom(cfg) { 35 if (!cfg || !cfg.tiers) return; 36 var byKey = {}; cfg.tiers.forEach(function (t) { byKey[t.key] = t; }); 37 document.querySelectorAll('[data-price]').forEach(function (el) { 38 var t = byKey[el.getAttribute('data-price')]; 39 if (!t) return; 40 // Discounted: show what it costs now, with the list price struck through 41 // beside it, so the saving is visible rather than just a smaller number. 42 if (t.early_price_cents != null && t.early_price_cents < t.price_cents) { 43 el.innerHTML = '<span class="was">' + rands(t.price_cents) + '</span> ' + rands(t.early_price_cents); 44 } else { 45 el.textContent = rands(t.price_cents); 46 } 47 }); 48 var prices = cfg.tiers.map(nowCents); 49 if (prices.length) { var min = Math.min.apply(null, prices); document.querySelectorAll('[data-price-from]').forEach(function (el) { el.textContent = rands(min); }); } 50 document.querySelectorAll('[data-includes]').forEach(function (el) { 51 var t = byKey[el.getAttribute('data-includes')]; 52 var items = t && t.includes ? t.includes.split('\n').map(function (x) { return x.trim(); }).filter(Boolean) : []; 53 var wrap = el.closest('[data-includes-wrap]'); 54 if (items.length) { el.innerHTML = items.map(function (li) { return '<li>' + li.replace(/&/g, '&').replace(/</g, '<') + '</li>'; }).join(''); if (wrap) wrap.style.display = ''; } 55 else if (wrap) { wrap.style.display = 'none'; } 56 }); 57 } 58 // window.NDLALA.EVENT is a hardcoded night-one fallback in site.js. Once the API 59 // has told us which night this page is actually selling, overwrite it, or the 60 // seat picker prints night one's date and day above night two's seats. 61 function updateEvent(cfg) { 62 var e = cfg && cfg.event, E = window.NDLALA && window.NDLALA.EVENT; 63 if (!e || !E) return; 64 if (e.slug) E.id = e.slug; 65 if (e.name) E.name = e.name; 66 if (e.tagline) E.tagline = e.tagline; 67 if (e.dayLabel) E.dayLabel = e.dayLabel; 68 if (e.doorsLabel) E.doors = e.doorsLabel; 69 if (e.venue) E.venue = e.venue; 70 if (e.address) E.address = e.address; 71 if (e.date) { 72 E.dateISO = String(e.date).slice(0, 10); 73 // The night is a plain calendar date â format it with no time zone at all, 74 // so it reads the same everywhere (see NDLALA_EV.plainDate). 75 if (window.NDLALA_EV && window.NDLALA_EV.plainDate) { 76 E.dateLabel = window.NDLALA_EV.plainDate(e.date, { day: 'numeric', month: 'long', year: 'numeric' }); 77 } 78 } 79 } 80 81 function whenReady(fn) { if (document.readyState !== 'loading') fn(); else document.addEventListener('DOMContentLoaded', fn); } 82 83 // Prices are per event now. Resolve which night this page is about first, so a 84 // night-two page never shows night-one pricing. 85 function cfgUrl() { 86 try { 87 var e = new URLSearchParams(location.search).get('e') || new URLSearchParams(location.search).get('event'); 88 if (e) return '/api/public/config?event=' + encodeURIComponent(e); 89 } catch (x) {} 90 try { 91 var c = JSON.parse(sessionStorage.getItem('ndlala_cart') || '{}'); 92 if (c && c.event) return '/api/public/config?event=' + encodeURIComponent(c.event); 93 } catch (x) {} 94 return null; 95 } 96 97 // No night in the link (the homepage Book Now, every What's On link) used to 98 // ask the API for its default, which is night one of the August run: a past 99 // event with no tiers, so the page kept the stale prices in its HTML. Thami saw 100 // R13 000 for Diamond on 6 Oct that way. Ask events.js which night is actually 101 // on sale instead, the same night event-page.js renders. 102 function resolveUrl() { 103 var u = cfgUrl(); 104 if (u) return Promise.resolve(u); 105 var EV = window.NDLALA_EV; 106 if (!EV || !EV.current) return Promise.resolve('/api/public/config'); 107 return EV.current().then(function (slug) { 108 return slug ? '/api/public/config?event=' + encodeURIComponent(slug) : '/api/public/config'; 109 }, function () { return '/api/public/config'; }); 110 } 111 112 window.NDLALA_CONFIG_READY = resolveUrl() 113 .then(function (u) { return fetch(u, { cache: 'no-store' }); }) 114 .then(function (r) { return r.ok ? r.json() : null; }) 115 .then(function (cfg) { 116 // Buyer-facing hold copy reads this instead of hardcoding a duration. 117 if (cfg && cfg.holdMinutes) window.NDLALA_HOLD_MINUTES = cfg.holdMinutes; 118 updateTiers(cfg); updateEvent(cfg); whenReady(function () { applyDom(cfg); }); return cfg; 119 }) 120 .catch(function () {}); 121})();
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.