1// ============================================================== 2// SUCHITRA KRISHNAMOORTHI LIVE SITE CONTENT ENGINE (V2 SAFE) 3// Connects frontend pages to live Admin CMS data with zero layout disruption 4// Strictly protects footers, masonry layouts, and Zyro grid architecture 5// ============================================================== 6 7(function () { 8 'use strict'; 9 10 let siteData = null; 11 12 document.addEventListener('DOMContentLoaded', initLiveContentEngine); 13 14 async function initLiveContentEngine() { 15 siteData = await fetchSiteContent(); 16 if (!siteData) return; 17 18 const path = window.location.pathname.replace(/\/$/, '') || '/'; 19 const pages = siteData.pages || {}; 20 21 // Route-specific hydrations & Page Settings 22 if (path.includes('my-books')) { 23 if (pages.books) applyPageHeaderSettings('books', pages.books); 24 hydrateBooksPage(siteData.books || []); 25 } else if (path.includes('blog')) { 26 if (pages.blogs) applyPageHeaderSettings('blogs', pages.blogs); 27 // NOTE: Blog articles & masonry are rendered statically by server.js / Zyro 28 // with native layout, #blog-post-sizer, and active blogs. 29 // We do NOT tamper with .posts-list-wrapper to preserve layout and navigation. 30 } else if (path.includes('my-music')) { 31 if (pages.music) applyPageHeaderSettings('music', pages.music); 32 } else if (path.includes('my-paintings') || path.includes('gallery')) { 33 if (pages.paintings) applyPageHeaderSettings('paintings', pages.paintings); 34 } else if (path.includes('my-poetry')) { 35 if (pages.poetry) applyPageHeaderSettings('poetry', pages.poetry); 36 } else if (path.includes('my-movies') || path.includes('my-plays')) { 37 if (pages.movies) applyPageHeaderSettings('movies', pages.movies); 38 } else if (path.includes('my-videos') || path.includes('double-occupancy-series')) { 39 if (pages.videos) applyPageHeaderSettings('videos', pages.videos); 40 } else if (path === '/' || path === '/index.html' || path === '') { 41 hydrateHomePage(siteData); 42 } 43 44 // Always ensure footer newsletter inputs have clear, full placeholders 45 fixFooterNewsletterInputs(); 46 } 47 48 // Footer Newsletter Form Input Label & Placeholder Fix 49 function fixFooterNewsletterInputs() { 50 const nameInputs = document.querySelectorAll('form input[name="field0"], form input[placeholder="Name"]'); 51 nameInputs.forEach(input => { 52 input.setAttribute('placeholder', 'Your Name'); 53 input.setAttribute('aria-label', 'Your Name'); 54 }); 55 56 const emailInputs = document.querySelectorAll('form input[name="field1"], form input[placeholder="Email"], form input[type="email"]'); 57 emailInputs.forEach(input => { 58 input.setAttribute('placeholder', 'Your Email Address'); 59 input.setAttribute('aria-label', 'Your Email Address'); 60 }); 61 } 62 63 // Apply Page Header & Banner Settings dynamically 64 // Strictly scoped to header containers only - NEVER touches footer or content grids! 65 function applyPageHeaderSettings(pageKey, pageData) { 66 if (!pageData) return; 67 68 const bannerContainer = document.getElementById('container-widget-1767804542652'); 69 70 // 1. Tagline / Category Badge button (inside banner container only) 71 if (pageData.badge && bannerContainer) { 72 const badge = bannerContainer.querySelector('#button-widget-1767804542651 span, .page-header-badge'); 73 if (badge) { 74 badge.innerText = pageData.badge; 75 badge.style.color = '#ffffff'; 76 } 77 } 78 79 // 2. Heading Title (pure white, DM Serif Display) 80 if (pageData.title) { 81 // Blog page header title 82 const blogHead = document.querySelector('#text-widget-1461519401626 h2'); 83 if (blogHead) { 84 blogHead.innerHTML = `<span style="font-size: 60px; font-family: 'DM Serif Display', serif; color: #ffffff !important;">${pageData.title}</span>`; 85 blogHead.style.color = '#ffffff'; 86 } 87 88 // Standard page banner heading 89 if (bannerContainer) { 90 const headH2 = bannerContainer.querySelector('#heading-widget-1767804542650 h2'); 91 if (headH2) { 92 headH2.innerHTML = `<span style="font-family: 'DM Serif Display', serif; font-size: 60px; color: #ffffff !important;">${pageData.title}</span>`;
93 headH2.style.color = '#ffffff'; 94 } 95 } 96 97 document.title = `${pageData.title} | Suchitra Krishnamoorthi`; 98 } 99 100 // 3. Subtitle (inside banner container only - pure white) 101 if (pageData.subtitle && bannerContainer) { 102 const subP = bannerContainer.querySelector('#text-widget-1767804542648 p'); 103 if (subP) { 104 subP.innerHTML = `<span style="color: #ffffff !important; font-size: 24px; font-style: italic; font-family: 'Playfair Display', serif;">${pageData.subtitle}</span>`; 105 subP.style.color = '#ffffff'; 106 } 107 } 108 109 // 4. Description / Intro text 110 if (pageData.description) { 111 const descEl = document.querySelector('.page-header-desc'); 112 if (descEl) descEl.innerText = pageData.description; 113 } 114 115 // 5. Banner Image - ONLY within specific banner container! 116 // Absolute rule: Never touch footer, body, or generic .grid-column! 117 if (pageData.banner_image) { 118 const cleanImg = resolveAssetUrl(pageData.banner_image); 119 120 // Standard pages (my-books, my-music, my-paintings, my-videos, my-movies, my-poetry) 121 if (bannerContainer) { 122 const bannerImg = bannerContainer.querySelector('#image-widget-1767804542646 img, .imageWidget img'); 123 if (bannerImg) { 124 bannerImg.src = cleanImg; 125 } 126 } 127 128 // Blog page header row (only if valid banner image and not meta thumbnail) 129 if (pageKey === 'blogs' && pageData.banner_image && !pageData.banner_image.includes('1-7727269')) { 130 const blogBanner = document.getElementById('container-widget-40'); 131 if (blogBanner) { 132 blogBanner.style.backgroundImage = `url("${cleanImg}")`; 133 blogBanner.style.backgroundSize = 'cover'; 134 blogBanner.style.backgroundPosition = 'center'; 135 } 136 } 137 } 138 } 139 140 // Fetch Live Content from Backend (with fallback) 141 async function fetchSiteContent() { 142 try { 143 const res = await fetch('/api/content'); 144 if (res.ok) { 145 return await res.json(); 146 } 147 } catch (e) {} 148 149 // Fallback to static JSON 150 try { 151 const fb = await fetch('/data/site-content.json'); 152 if (fb.ok) { 153 return await fb.json(); 154 } 155 } catch (e) {} 156 157 return null; 158 } 159 160 // Asset URL Helper 161 function resolveAssetUrl(url) { 162 if (!url) return '/assets/images/wp-uploads/108-quotes-gita.webp'; 163 if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:')) return url;
164 let clean = url.replace(/^(\.\.\/)+/, '').replace(/^\.\//, '').replace(/^\//, ''); 165 return '/' + clean; 166 } 167 168 // ------------------------------------------------------------- 169 // 1. HYDRATE BOOKS PAGE (/my-books) 170 // ------------------------------------------------------------- 171 function hydrateBooksPage(books) { 172 if (!books || books.length === 0) return; 173 174 const targetContainer = document.getElementById('container-widget-1767811439526'); 175 if (!targetContainer) return; 176 177 targetContainer.innerHTML = ` 178 <div style="max-width: 1200px; margin: 0 auto; padding: 20px 20px 60px;"> 179 <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 32px;"> 180 ${books.map(b => ` 181 <div class="suchitra-book-card" style="background: #ffffff; border-radius: 16px; overflow: hidden; box-shadow: 0 6px 25px rgba(0,0,0,0.07); border: 1px solid #f1f5f9; display: flex; flex-direction: column; transition: transform 0.3s ease, box-shadow 0.3s ease;"> 182 <div style="position: relative; overflow: hidden; height: 360px; background: #faf8f5; display: flex; align-items: center; justify-content: center; padding: 16px;"> 183 <img src="${resolveAssetUrl(b.image)}" alt="${b.title}" style="max-height: 100%; max-width: 100%; object-fit: contain; filter: drop-shadow(0 10px 15px rgba(0,0,0,0.15)); transition: transform 0.3s ease;" onerror="this.src='/assets/images/wp-uploads/108-quotes-gita.webp'"> 184 <span style="position: absolute; top: 14px; left: 14px; background: rgba(15, 23, 42, 0.85); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; padding: 4px 10px; border-radius: 20px; letter-spacing: 0.5px;">${b.category || 'Book'}</span> 185 </div> 186 <div style="padding: 24px; flex: 1; display: flex; flex-direction: column; justify-content: space-between;"> 187 <div> 188 <h3 style="font-family: 'DM Serif Display', serif; font-size: 21px; line-height: 1.35; margin: 0 0 10px; color: #1e293b;">${b.title}</h3> 189 <p style="color: #64748b; font-size: 14px; line-height: 1.6; margin: 0 0 18px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${b.description || ''}</p> 190 </div> 191 <div> 192 <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px;"> 193 <div style="font-size: 22px; font-weight: 800; color: #eb6a56;">${b.price ? 'â¹' + b.price : 'Special Edition'}</div> 194 <span style="font-size: 12px; color: #10b981; font-weight: 700;"><i class="fas fa-check-circle"></i> In Stock</span> 195 </div> 196 <a href="/checkout?product=${b.id}" style="display: block; text-align: center; background: #eb6a56; color: #ffffff; padding: 12px 20px; border-radius: 30px; font-weight: 700; text-decoration: none; font-size: 14px; transition: background 0.2s ease;"> 197 <i class="fas fa-shopping-bag"></i> Order Signed Copy 198 </a> 199 </div> 200 </div> 201 </div> 202 `).join('')} 203 </div> 204 </div> 205 `; 206 } 207 208 // ------------------------------------------------------------- 209 // 2. HYDRATE HOMEPAGE (/) 210 // ------------------------------------------------------------- 211 function hydrateHomePage(data) { 212 if (!data) return; 213 214 if (data.homepage) { 215 const hp = data.homepage; 216 const heroHead = document.querySelector('.theme-hero h1') || document.querySelector('h1'); 217 if (heroHead && hp.hero_title_1) { 218 heroHead.innerHTML = hp.hero_title_1 + (hp.hero_title_2 ? '<br>' + hp.hero_title_2 : ''); 219 } 220 } 221 } 222 223})();
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.