/**
* 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();
}