1document.addEventListener('DOMContentLoaded',function(){const basePath=window.MONEYPLANTS_BASE_PATH||'';const API_BASE_URL=basePath+'academy/api.php';const LOGO_PATH=window.MONEYPLANTS_LOGO_PATH||'assets/img/logo.png';const PLACEHOLDER_PATH=window.MONEYPLANTS_BLOG_PLACEHOLDER_PATH||'assets/img/blog/blog-placeholder.webp';const preloader=document.getElementById('preloader');const postsContainer=document.getElementById('blog-posts-container');const paginationContainer=document.getElementById('blog-pagination-container');const searchForm=document.getElementById('blog-search-form');const searchInput=document.getElementById('blog-search-input');const postDetailContentArea=document.getElementById('blog-detail-content-area');const blogDetailTitleEl=document.getElementById('blog-detail-page-title');const blogDetailDescriptionEl=document.getElementById('blog-detail-description');function escapeHtml(unsafe){if(unsafe===null||typeof unsafe==='undefined')return'';return unsafe.toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")} 2function showPreloader(){if(preloader)preloader.style.display='flex'} 3function hidePreloader(){if(preloader)preloader.style.display='none'} 4function getYouTubeVideoID(url){if(!url)return null;const regExp=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;const match=url.match(regExp);return(match&&match[2].length===11)?match[2]:null} 5function setMetaTags(title,description){document.title=title?`${escapeHtml(title)} - MoneyPlants.in Blog`:"MoneyPlants.in Blog";const metaDesc=document.querySelector('meta[name="description"]');if(metaDesc){metaDesc.setAttribute('content',escapeHtml(description).substring(0,300))}} 6function displayPosts(data){if(!postsContainer)return;postsContainer.innerHTML='';if(!data.posts||data.posts.length===0){postsContainer.innerHTML=` 7 <div class="col-12 text-center py-5"> 8 <div class="mb-3"><i class="bi bi-search" style="font-size: 2rem; color: #ccc;"></i></div> 9 <h4 class="text-muted">No posts found.</h4> 10 <p>We couldn't find any articles matching your search. Try different keywords.</p> 11 ${searchInput && searchInput.value ? '<button id="btn-reset-search" class="btn btn-outline-primary mt-2 btn-sm">Clear Search</button>' : ''} 12 </div>`;const resetBtn=document.getElementById('btn-reset-search');if(resetBtn){resetBtn.addEventListener('click',()=>{if(searchInput)searchInput.value='';updateUrlAndFetch(1,'')})} 13if(paginationContainer)paginationContainer.innerHTML='';hidePreloader();return} 14data.posts.forEach(post=>{const postElement=document.createElement('div');postElement.className='col';let postDate='Recently';if(post.date){const dateObj=new Date(post.date);if(!isNaN(dateObj.getTime())){postDate=dateObj.toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'})}} 15const authorName=escapeHtml(post.Author||'MoneyPlants Team');const postTitle=escapeHtml(post.title||'Untitled Post');const authorIconPath=post.AuthorIcon?escapeHtml(post.AuthorIcon):LOGO_PATH;let mediaPreviewHTML='';if(post.Image){mediaPreviewHTML=`<img src="${escapeHtml(post.Image)}" alt="${postTitle}" onerror="this.onerror=null;this.src='${PLACEHOLDER_PATH}';">`}else if(post.YouTube){const videoId=getYouTubeVideoID(post.YouTube);mediaPreviewHTML=videoId?`<img src="https://i.ytimg.com/vi/${videoId}/hqdefault.jpg" alt="${postTitle}">`:`<img src="${PLACEHOLDER_PATH}" alt="${postTitle}">`}else{mediaPreviewHTML=`<img src="${PLACEHOLDER_PATH}" alt="${postTitle}">`} 16let descriptionPreview=post.description?escapeHtml(post.description):'Click to read more...';if(descriptionPreview.length>120){descriptionPreview=descriptionPreview.substring(0,120)+'...'} 17const tagsArray=Array.isArray(post.tags)?post.tags:(post.tags?String(post.tags).split(','):[]);const tagsString=tagsArray.length>0&&tagsArray[0].trim()!==""?escapeHtml(tagsArray[0].trim()):'General';postElement.innerHTML=` 18 <article> 19 <div class="post-img"> 20 ${mediaPreviewHTML} 21 </div> 22 <div class="post-content"> 23 <div class="post-meta mb-2 d-flex justify-content-between align-items-center">
24 <span class="post-category">${tagsString}</span> 25 <time class="post-date text-muted small">${postDate}</time> 26 </div> 27 <h2 class="title h5 mb-2"> 28 <a href="blog-details.php?slug=${escapeHtml(post.slug)}" class="stretched-link">${postTitle}</a> 29 </h2> 30 <p class="mb-0 text-muted small">${descriptionPreview}</p> 31 </div> 32 </article> 33 `;postsContainer.appendChild(postElement)});renderPagination(data.current_page,data.total_pages,searchInput?searchInput.value.trim():'');hidePreloader()} 34function renderPagination(currentPage,totalPages,currentQuery){if(!paginationContainer)return;paginationContainer.innerHTML='';if(totalPages<=1)return;const createPageLink=(page,iconOrText)=>{const li=document.createElement('li');if(page===currentPage)li.className='active';const a=document.createElement('a');a.href='#';a.innerHTML=iconOrText;a.addEventListener('click',(e)=>{e.preventDefault();if(page!==currentPage){updateUrlAndFetch(page,currentQuery)}});li.appendChild(a);return li};if(currentPage>1){paginationContainer.appendChild(createPageLink(currentPage-1,'<i class="bi bi-chevron-left"></i>'))} 35const startPage=Math.max(1,currentPage-1);const endPage=Math.min(totalPages,currentPage+1);if(startPage>1){paginationContainer.appendChild(createPageLink(1,1));if(startPage>2){const dots=document.createElement('li');dots.innerHTML='<a href="#" onclick="return false;">...</a>';paginationContainer.appendChild(dots)}} 36for(let i=startPage;i<=endPage;i++){paginationContainer.appendChild(createPageLink(i,i))} 37if(endPage<totalPages){if(endPage<totalPages-1){const dots=document.createElement('li');dots.innerHTML='<a href="#" onclick="return false;">...</a>';paginationContainer.appendChild(dots)} 38paginationContainer.appendChild(createPageLink(totalPages,totalPages))} 39if(currentPage<totalPages){paginationContainer.appendChild(createPageLink(currentPage+1,'<i class="bi bi-chevron-right"></i>'))}} 40function fetchBlogPosts(page=1,query=''){showPreloader();let url=`${API_BASE_URL}?action=list_posts&page=${page}&per_page=12`;if(query){url=`${API_BASE_URL}?action=search_posts&query=${encodeURIComponent(query)}&page=${page}&per_page=12`} 41fetch(url).then(response=>{if(!response.ok)throw new Error('Network response was not ok');return response.json()}).then(data=>{if(data.error)throw new Error(data.error);displayPosts(data)}).catch(error=>{console.error('Error fetching posts:',error);if(postsContainer)postsContainer.innerHTML='<div class="col-12 text-center text-danger py-5"><h4>Could not load posts.</h4><p>Please check your connection.</p></div>';hidePreloader()})} 42function updateUrlAndFetch(page,query){const newUrl=`${window.location.pathname}?query=${encodeURIComponent(query)}&page=${page}`;window.history.pushState({page:page,query:query},'',newUrl);fetchBlogPosts(page,query);const searchSection=document.querySelector('.blog-search-container');if(searchSection){const topPos=searchSection.getBoundingClientRect().top+window.scrollY-120;window.scrollTo({top:topPos,behavior:'smooth'})}else{window.scrollTo({top:0,behavior:'smooth'})}} 43function displayPostDetail(post){if(!postDetailContentArea)return;const postTitle=escapeHtml(post.title);let postDate='Recently';if(post.date){const dateObj=new Date(post.date);if(!isNaN(dateObj.getTime())){postDate=dateObj.toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'})}} 44const authorName=escapeHtml(post.Author||'MoneyPlants Team');const authorIconPath=post.AuthorIcon?escapeHtml(post.AuthorIcon):LOGO_PATH;const authorBio=post.author_bio?escapeHtml(post.author_bio):'We help simplify accounting for Mandi merchants.';setMetaTags(postTitle,escapeHtml(post.description));if(blogDetailTitleEl){const titleStr=post.title||'';blogDetailTitleEl.textContent=titleStr;if(titleStr.length>80){blogDetailTitleEl.style.fontSize='clamp(1.4rem, 3.2vw, 2.0rem)';blogDetailTitleEl.style.lineHeight='1.3'}else if(titleStr.length>50){blogDetailTitleEl.style.fontSize='clamp(1.7rem, 3.8vw, 2.3rem)';blogDetailTitleEl.style.lineHeight='1.3'}else{blogDetailTitleEl.style.fontSize='';blogDetailTitleEl.style.lineHeight=''}} 45if(blogDetailDescriptionEl){const descStr=post.description||'';blogDetailDescriptionEl.textContent=descStr;if(descStr.length>180){blogDetailDescriptionEl.style.fontSize='0.95rem';blogDetailDescriptionEl.classList.remove('fs-5')}else if(descStr.length>120){blogDetailDescriptionEl.style.fontSize='1.05rem';blogDetailDescriptionEl.classList.remove('fs-5')}else{blogDetailDescriptionEl.style.fontSize='';blogDetailDescriptionEl.classList.add('fs-5')}} 46let mediaFullHTML='';if(post.Image){mediaFullHTML=`<img src="${escapeHtml(post.Image)}" alt="${postTitle}" onerror="this.onerror=null;this.src='${PLACEHOLDER_PATH}';">`}else if(post.YouTube){const videoId=getYouTubeVideoID(post.YouTube);mediaFullHTML=videoId?`<div class="ratio ratio-16x9"><iframe src="https://www.youtube.com/embed/${videoId}" title="YouTube video player" frameborder="0" allowfullscreen></iframe></div>`:`<img src="${PLACEHOLDER_PATH}" alt="${postTitle}">`}else{mediaFullHTML=`<img src="${PLACEHOLDER_PATH}" alt="${postTitle}">`} 47const tagsArray=Array.isArray(post.tags)?post.tags:(post.tags?String(post.tags).split(','):[]);let tagsHTML='';tagsArray.forEach(tag=>{const t=tag.trim();if(t)tagsHTML+=`<a href="blog.php?query=${encodeURIComponent(t)}">${escapeHtml(t)}
47</a>`});postDetailContentArea.innerHTML=` 48 <article class="article"> 49 <div class="entry-media-full mb-4" id="post-media-full"> 50 ${mediaFullHTML} 51 </div> 52 53 <h2 class="title">${postTitle}</h2> 54 55 <div class="content mt-3"> 56 ${post.content} 57 </div> 58 59 <div class="meta-bottom"> 60 <i class="bi bi-tags"></i> 61 <ul class="tags"> 62 <li>${tagsHTML || '<a href="blog.php">Article</a>'}</li> 63 </ul> 64 </div> 65 </article> 66 67 <section class="blog-author mt-4"> 68 <div class="author-container"> 69 <img src="${authorIconPath}" alt="${authorName}" onerror="this.onerror=null;this.src='${LOGO_PATH}';"> 70 <div> 71 <h4>${authorName}</h4> 72 <div class="social-links"> 73 <a href="https://wa.me/918279111008" target="_blank" rel="noopener noreferrer"><i class="bi bi-whatsapp"></i></a> 74 <a href="https://www.instagram.com/moneyplants.in" target="_blank" rel="noopener noreferrer"><i class="bi bi-instagram"></i></a> 75 <a href="https://www.youtube.com/@MoneyPlants-in" target="_blank" rel="noopener noreferrer"><i class="bi bi-youtube"></i></a> 76 <a href="https://www.facebook.com/people/Moneyplants/61590477064064/" target="_blank" rel="noopener noreferrer"><i class="bi bi-facebook"></i></a> 77 </div> 78 <p>${authorBio}</p> 79 </div> 80 </div> 81 </section> 82 `;hidePreloader()} 83function fetchPostDetail(slug){showPreloader();fetch(`${API_BASE_URL}?action=get_post&slug=${slug}`).then(response=>response.json()).then(data=>{if(data.error)throw new Error(data.error);displayPostDetail(data)}).catch(error=>{console.error('Error fetching post:',error);if(postDetailContentArea)postDetailContentArea.innerHTML=`<div class="alert alert-danger text-center"><strong>Error:</strong> Post not found or removed. <br><a href="blog.php" class="btn btn-outline-danger mt-2">Return to Blog</a></div>`;hidePreloader()})} 84function initFaq(){const faqItems=document.querySelectorAll('.faq-item');if(!faqItems.length)return;faqItems.forEach(faqItem=>{if(!faqItem.dataset.faqInitialized){faqItem.dataset.faqInitialized="true";faqItem.addEventListener('click',(e)=>{if(e.target.closest('.faq-content')){return} 85faqItem.classList.toggle('faq-active')})}})} 86initFaq();if(postsContainer){const urlParams=new URLSearchParams(window.location.search);const queryFromUrl=urlParams.get('query')||'';const pageFromUrl=parseInt(urlParams.get('page'),10)||1;if(searchInput)searchInput.value=queryFromUrl;fetchBlogPosts(pageFromUrl,queryFromUrl);window.addEventListener('popstate',function(event){const state=event.state;if(state){if(searchInput)searchInput.value=state.query||'';fetchBlogPosts(state.page,state.query)}else{fetchBlogPosts(1,'')}});if(searchForm){searchForm.addEventListener('submit',function(e){e.preventDefault();const q=searchInput.value.trim();updateUrlAndFetch(1,q)})}}else if(postDetailContentArea){const slug=new URLSearchParams(window.location.search).get('slug');const ssrContentExists=postDetailContentArea.querySelector('.post-content-body');if(slug&&!ssrContentExists){fetchPostDetail(slug)}else if(!slug){postDetailContentArea.innerHTML='<div class="alert alert-warning text-center">No post specified. <a href="blog.php">Go back to Blog</a></div>';hidePreloader()}else{hidePreloader()}}else{hidePreloader()}})
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.