/** * BLOG_DATA - Data fetched from Digitalsbooster Blog Database * Initialized from server-side API */ var BLOG_DATA = BLOG_DATA || []; // Initialize as empty array, will be populated via API /** * Create skeleton loader HTML that matches actual content structure */ function createSkeletonLoader() { return ` `; } /** * Create featured post skeleton */ function createFeaturedSkeleton() { return ` ${createSkeletonLoader()}
`; } /** * Create post card skeleton */ function createPostCardSkeleton() { return `
`; } /** * Create mini-card skeleton (used in trending/sidebar sections) */ function createMiniCardSkeleton() { return `
`; } /** * Create single post skeleton (Matched to screenshot) */ function createSinglePostSkeleton() { return ` ${createSkeletonLoader()}

`; } /** * Convert relative image paths to absolute URLs */ function getAbsoluteImageUrl(imagePath) { if (!imagePath) return '../assets/images/placeholder.png'; // If already absolute URL, return as is if (imagePath.startsWith('http://') || imagePath.startsWith('https:///')) { return imagePath; } // Clean up the path: remove leading ../ or / let cleanPath = imagePath.replace(/^(\.\.\/)+/, '').replace(/^\//, ''); // If it starts with dashboard/ or uploads/, prepend blog/ if (cleanPath.startsWith('dashboard/')) { cleanPath = 'blogs/' + cleanPath; } // Get prefix based on page location const loc = window.location.pathname; let prefix = ''; if (loc.match(/\/blogs\/post\//i)) { prefix = '../index.html'; } else if (loc.match(/\/(blogs|company|services)\//i)) { prefix = '../'; } return prefix + cleanPath; } /** * Process blog content HTML to fix relative image paths and remove contenteditable attributes */ function processContentHTML(htmlContent) { if (!htmlContent) return ''; let processed = htmlContent; // Replace relative image src paths in editor-image-wrapper and regular img tags processed = processed.replace(/src="(?!https?:\/\/)([^"]+)"/g, (match, path) => { const absolutePath = getAbsoluteImageUrl(path); return `src="${absolutePath}"`; }); // Remove contenteditable attributes from content processed = processed.replace(/contenteditable="true"/g, 'contenteditable="false"'); processed = processed.replace(/contenteditable="false"/g, ''); // Remove data-placeholder attributes to prevent input-like appearance processed = processed.replace(/data-placeholder="[^"]*"/g, ''); // Add CSS class to make image captions read-only processed = processed.replace(/class="image-caption"/g, 'class="image-caption" style="pointer-events: none; user-select: none; color: #999; font-size: 0.9rem; padding: 8px 0;"'); // Make editor-image-wrapper display as static content processed = processed.replace(/class="editor-image-wrapper"/g, 'class="editor-image-wrapper" style="margin: 2rem 0; border-radius: 8px; overflow: hidden;"'); return processed; } /** * Fetch blogs data from the server database * Populates BLOG_DATA with data from blogs table */ async function initializeBlogData() { // If data was already provided by PHP (SSR), don't fetch again if (window.BLOG_DATA && window.BLOG_DATA.length > 0) { console.log('✓ Using blog data provided by SSR'); BLOG_DATA = window.BLOG_DATA; // Dispatch custom event to notify that data is loaded window.dispatchEvent(new CustomEvent('blogDataLoaded', { detail: { count: BLOG_DATA.length } })); return BLOG_DATA; } try { // Determine path prefix based on directory depth const loc = window.location.pathname; let prefix = ''; if (loc.match(/\/blogs\/post/i)) { prefix = '../index.html'; } else if (loc.match(/\/(blogs|company|services)/i)) { prefix = '../'; } const response = await fetch(prefix + 'blogs/dashboard/api/get-all-blogs.php'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); if (result.success && result.data && Array.isArray(result.data)) { BLOG_DATA = result.data.map(blog => ({ id: blog.id || blog.slug, title: blog.title, slug: blog.slug, excerpt: blog.excerpt, body: processContentHTML(blog.content), content: processContentHTML(blog.content), author: blog.author?.name || blog.authorName || 'Unknown', author_id: blog.author_id, authorData: blog.author ? { ...blog.author, image: getAbsoluteImageUrl(blog.author?.image) } : null, date: blog.date, created_at: blog.created_at, updated_at: blog.updated_at, category: Array.isArray(blog.tags) ? blog.tags : (blog.category || []), tags: Array.isArray(blog.tags) ? blog.tags : [], imageURL: getAbsoluteImageUrl(blog.featured_image || blog.imageURL), featured_image: getAbsoluteImageUrl(blog.featured_image), authorImage: getAbsoluteImageUrl(blog.author?.image || blog.authorImage), status: blog.status, disclaimer: blog.disclaimer, views: blog.views || 0 })); window.BLOG_DATA = BLOG_DATA; // Dispatch custom event to notify that data is loaded window.dispatchEvent(new CustomEvent('blogDataLoaded', { detail: { count: BLOG_DATA.length } })); return BLOG_DATA; } else { throw new Error(result.error || 'Failed to load blog data'); } } catch (error) { console.error('❌ Error loading blog data:', error); // Dispatch error event window.dispatchEvent(new CustomEvent('blogDataError', { detail: { error: error.message } })); return []; } } /** * Get a single blog by ID or slug */ function getBlogById(idOrSlug) { return BLOG_DATA.find(blog => blog.id === idOrSlug || blog.slug === idOrSlug); } /** * Get blogs filtered by category/tags */ function getBlogsByCategory(category) { return BLOG_DATA.filter(blog => blog.tags && blog.tags.includes(category) ); } /** * Get blogs by author */ function getBlogsByAuthor(authorId) { return BLOG_DATA.filter(blog => blog.author_id === authorId); } /** * Search blogs by title or excerpt */ function searchBlogs(query) { const lowerQuery = query.toLowerCase(); return BLOG_DATA.filter(blog => blog.title.toLowerCase().includes(lowerQuery) || blog.excerpt.toLowerCase().includes(lowerQuery) ); } // Initialize blog data when script loads if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeBlogData); } else { // DOM is already loaded initializeBlogData(); }