1/** 2 * Artist tool live preview via postMessage 3 * Listens for style updates from the admin artist tool iframe parent. 4 */ 5(function() { 6 function fmtNum(n) { return Math.round(n).toLocaleString(); } 7 8 function rebuildPriceCard(listingType, ltoData) { 9 var card = document.querySelector('[data-card="price"]'); 10 var content = document.getElementById('priceCardContent'); 11 if (!card || !content) return; 12 13 var buyNowPrice = card.dataset.buyNowPrice; 14 var estimatedHigh = card.dataset.estimatedHigh; 15 var isSeller = card.dataset.isSeller === '1'; 16 var certId = card.dataset.certId; 17 var showAppraisal = card.dataset.showAppraisal !== '0'; 18 19 card.dataset.listingType = listingType; 20 21 var appraisalHtml; 22 if (!showAppraisal) { 23 appraisalHtml = ''; 24 } else if (certId) { 25 appraisalHtml = '<a href="#" class="cert-link" data-cert-id="' + certId + '">Appraised at $' + estimatedHigh + ' <i class="fas fa-certificate" style="font-size:0.65em;opacity:0.6"></i></a>'; 26 } else { 27 appraisalHtml = 'Appraised at $' + estimatedHigh; 28 } 29 30 var html = ''; 31 var hasBuyNow = buyNowPrice && (listingType === 'buy_now' || listingType === 'buy_now_or_offer'); 32 33 if (hasBuyNow) { 34 html += '<div class="factor-label price-label">Price</div>'; 35 html += '<div class="price-tag price-amount">$' + buyNowPrice + '</div>'; 36 if (appraisalHtml) html += '<div class="text-xs text-light price-appraisal">' + appraisalHtml + '</div>'; 37 if (!isSeller) { 38 html += '<button class="btn btn-primary btn-mb" id="buyNowBtn"><i class="fas fa-shopping-cart"></i> Buy Now</button>'; 39 // LTO add-on: shown alongside Buy Now when the listing offers lease-to-own. 40 if (ltoData && ltoData.monthly_payment > 0) { 41 html += '<div class="lto-addon-option" style="margin-top:14px;padding-top:14px;border-top:1px solid rgba(255,255,255,0.08);">'; 42 html += '<div class="text-sm" style="margin-bottom:6px;">or <strong>lease to own</strong> from <strong>$' + fmtNum(ltoData.monthly_payment) + '/mo</strong> × ' + ltoData.num_months + ' months' + (ltoData.down_payment > 0 ? ' · $' + fmtNum(ltoData.down_payment) + ' down' : '') + '</div>'; 43 html += '<button class="btn btn-outline btn-mb" id="ltoStartBtn"><i class="fas fa-file-signature"></i> Lease to Own</button>'; 44 html += '</div>'; 45 } 46 } 47 } else if (listingType === 'lease_to_own' && ltoData && ltoData.monthly_payment > 0) { 48 html += '<div class="factor-label price-label">Lease to Own</div>'; 49 html += '<div class="price-tag price-amount">$' + fmtNum(ltoData.monthly_payment) + '<span class="lto-per-month">/mo</span></div>'; 50 html += '<div class="text-xs text-light price-appraisal">' + ltoData.num_months + ' months · $' + fmtNum(ltoData.total_price) + ' total</div>'; 51 if (ltoData.down_payment > 0) { 52 html += '<div class="text-xs text-light lto-down-payment">$' + fmtNum(ltoData.down_payment) + ' due today</div>'; 53 } 54 if (appraisalHtml) html += '<div class="text-xs text-muted price-appraisal">' + appraisalHtml + '</div>'; 55 if (!isSeller) { 56 html += '<button class="btn btn-primary btn-mb" id="ltoStartBtn"><i class="fas fa-file-signature"></i> Start Lease</button>'; 57 } 58 } else if (listingType === 'make_offer') { 59 html += '<div class="factor-label price-label">Make an Offer</div>'; 60 if (appraisalHtml) html += '<div class="text-sm text-muted price-appraisal">' + appraisalHtml + '</div>'; 61 } else { 62 html += '<div class="factor-label price-label">Price on Inquiry</div>'; 63 if (appraisalHtml) html += '<div class="text-sm text-muted price-appraisal">' + appraisalHtml + '</div>'; 64 } 65 66 if (!isSeller && (listingType === 'make_offer' || listingType === 'buy_now_or_offer')) { 67 html += '<button class="btn btn-outline btn-mb" id="makeOfferBtn"><i class="fas fa-hand-holding-usd"></i> Make Offer</button>'; 68 } 69 70 if (!isSeller && listingType === 'inquiry') { 71 html += '<button class="btn btn-primary btn-mb" id="inquireBtn"><i class="fas fa-envelope"></i> Inquire About Price</button>'; 72 } 73 74 content.innerHTML = html; 75 76 var stripeNote = card.querySelector('.price-stripe-note'); 77 if (stripeNote) stripeNote.hidden = (listingType === 'inquiry'); 78 79 var mobileCta = document.querySelector('.mobile-cta'); 80 if (mobileCta && !isSeller) { 81 var ctaText = mobileCta.querySelector('.mobile-cta-text'); 82 var ctaBtn = mobileCta.querySelector('.btn-cta'); 83 var isLto = listingType === 'lease_to_own' && ltoData && ltoData.monthly_payment > 0; 84 if (ctaText) { 85 if (hasBuyNow) { 86 ctaText.innerHTML = '<div class="mobile-cta-price">$' + buyNowPrice + '</div>'; 87 } else if (isLto) { 88 ctaText.innerHTML = '<div class="mobile-cta-price">$' + fmtNum(ltoData.monthly_payment) + '/mo</div>'; 89 } else if (listingType === 'inquiry') { 90 ctaText.innerHTML = '<div class="mobile-cta-label">Inquire About Price</div>'; 91 } else { 92 ctaText.innerHTML = '<div class="mobile-cta-label">Make an Offer</div>'; 93 } 94 } 95 if (ctaBtn) { 96 if (hasBuyNow) { 97 ctaBtn.textContent = 'Buy Now'; 98 } else if (isLto) { 99 ctaBtn.textContent = 'Start Lease'; 100 } else if (listingType === 'make_offer' || listingType === 'buy_now_or_offer') { 101 ctaBtn.textContent = 'Make Offer'; 102 } else { 103 ctaBtn.textContent = 'Inquire'; 104 } 105 } 106 } 107 } 108 109 window.addEventListener('message', function(e) { 110 if (!e.data) return; 111 112 if (e.data.type === 'artist-listing-type') { 113 rebuildPriceCard(e.data.listingType, e.data.lto || null); 114 return; 115 } 116 117 if (e.data.type !== 'artist-style') return; 118 var root = document.documentElement; 119 var d = e.data; 120 121 /* Apply theme variables */ 122 if (d.vars) {
123 Object.keys(d.vars).forEach(function(k) { 124 root.style.setProperty('--' + k, d.vars[k]); 125 }); 126 } 127 128 /* Apply border styles to cards */ 129 if (d.border) { 130 var cards = document.querySelectorAll('.card, .card-flush'); 131 cards.forEach(function(card) { 132 if (d.border.style === 'none') { 133 card.style.border = 'none'; 134 } else { 135 card.style.border = d.border.width + ' ' + d.border.style + ' ' + (d.border.color || 'var(--border)'); 136 } 137 if (d.border.radius) card.style.borderRadius = d.border.radius; 138 if (d.border.shadow !== undefined) card.style.boxShadow = d.border.shadow; 139 }); 140 } 141 142 /* Apply background image as the deepest page layer. 143 The page bg color sits on top with opacity controlled by the slider, 144 letting the image show through. */ 145 var bgLayer = document.getElementById('artist-bg-layer'); 146 if (!bgLayer) { 147 bgLayer = document.createElement('div'); 148 bgLayer.id = 'artist-bg-layer'; 149 bgLayer.style.cssText = 'position:fixed;inset:0;z-index:-2;background-size:cover;background-position:center;pointer-events:none;'; 150 document.body.prepend(bgLayer); 151 } 152 var bgOverlay = document.getElementById('artist-bg-overlay'); 153 if (!bgOverlay) { 154 bgOverlay = document.createElement('div'); 155 bgOverlay.id = 'artist-bg-overlay'; 156 bgOverlay.style.cssText = 'position:fixed;inset:0;z-index:-1;pointer-events:none;'; 157 document.body.insertBefore(bgOverlay, bgLayer.nextSibling); 158 } 159 160 if (d.bgImage) { 161 var opacity = d.bgOpacity !== undefined ? d.bgOpacity : 0.15; 162 bgLayer.style.backgroundImage = 'url(' + d.bgImage + ')'; 163 var bgColor = getComputedStyle(root).getPropertyValue('--bg').trim() || '#ffffff'; 164 bgOverlay.style.backgroundColor = bgColor; 165 bgOverlay.style.opacity = 1 - opacity; 166 document.body.style.setProperty('background-color', 'transparent', 'important'); 167 } else { 168 bgLayer.style.backgroundImage = ''; 169 bgOverlay.style.backgroundColor = ''; 170 bgOverlay.style.opacity = ''; 171 // Override the page-injected `body { background: transparent !important }` rule 172 // (emitted by listing.php when the listing was loaded with a bgImage). Without 173 // !important here the body stays transparent and the browser default shows 174 // through, which often reads as dark/black in light theme. Pull from --bg so 175 // it matches the current theme. 176 document.body.style.setProperty('background-color', 'var(--bg)', 'important'); 177 } 178 179 /* Force theme mode */ 180 if (d.theme) { 181 root.setAttribute('data-theme', d.theme); 182 } 183 }); 184})();
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.