/* /assets/js/main.js - PromptVerse AI */
'use strict';
const PV = {
siteUrl: $('meta[name="site-url"]').attr('content') || '',
csrf: $('meta[name="csrf-token"]').attr('content') || '',
// ── Toast ──────────────────────────────────────────────────────────────────
toast(msg, type = 'success', duration = 3500) {
const id = 'toast_' + Date.now();
const icon = type === 'success' ? 'bi-check-circle-fill text-success' : type === 'error' ? 'bi-x-circle-fill text-danger' : 'bi-info-circle-fill text-primary';
const cls = type === 'success' ? 'success-toast' : type === 'error' ? 'error-toast' : '';
const html = `
`;
$('#toastContainer').append(html);
setTimeout(() => $('#' + id).remove(), duration);
},
// ── Copy countdown + voice ─────────────────────────────────────────────────
copyCountdown(promptId, promptText) {
const modal = new bootstrap.Modal(document.getElementById('copyModal'));
const circle = document.getElementById('countdownCircle');
const textEl = document.getElementById('countdownText');
const display = document.getElementById('countdownDisplay');
const progress = document.getElementById('copyProgress');
const total = 339.3; // 2πr
let seconds = 5;
modal.show();
textEl.textContent = seconds;
display.textContent = seconds;
progress.style.width = '100%';
const tick = setInterval(() => {
seconds--;
const pct = (seconds / 5);
circle.style.strokeDashoffset = total * (1 - pct);
progress.style.width = (pct * 100) + '%';
textEl.textContent = seconds;
display.textContent = seconds;
if (seconds <= 0) {
clearInterval(tick);
modal.hide();
PV.doCopy(promptId, promptText);
}
}, 1000);
// Cancel on modal hide
document.getElementById('copyModal').addEventListener('hide.bs.modal', () => {
clearInterval(tick);
}, { once: true });
},
doCopy(promptId, promptText) {
// Copy to clipboard
navigator.clipboard.writeText(promptText).then(() => {
PV.toast('Prompt copied! Paste it into your AI tool.', 'success');
PV.voiceBangla('আপনার প্রম্পট কপি হয়েছে');
PV.trackCopy(promptId);
}).catch(() => {
// Fallback
const ta = document.createElement('textarea');
ta.value = promptText; ta.style.position = 'fixed'; ta.style.opacity = '0';
document.body.appendChild(ta); ta.select();
try { document.execCommand('copy'); PV.toast('Prompt copied!', 'success'); PV.voiceBangla('আপনার প্রম্পট কপি হয়েছে'); PV.trackCopy(promptId); }
catch { PV.toast('Copy failed. Please copy manually.', 'error'); }
document.body.removeChild(ta);
});
},
// ── Voice Notification ─────────────────────────────────────────────────────
voiceBangla(text) {
if (!('speechSynthesis' in window)) return;
window.speechSynthesis.cancel();
const utt = new SpeechSynthesisUtterance(text);
utt.lang = 'bn-BD';
utt.volume = 1;
utt.rate = 0.9;
utt.pitch = 1;
// Try Bengali voice
const voices = window.speechSynthesis.getVoices();
const bnVoice = voices.find(v => v.lang.includes('bn') || v.name.toLowerCase().includes('bangla'));
if (bnVoice) utt.voice = bnVoice;
window.speechSynthesis.speak(utt);
},
// ── Track copy via AJAX ────────────────────────────────────────────────────
trackCopy(promptId) {
$.post(PV.siteUrl + '/ajax/copy.php', { prompt_id: promptId, csrf: PV.csrf })
.done(res => {
try {
const d = JSON.parse(res);
if (d.copy_count !== undefined) {
// Update copy count on page
$(`.copy-count-${promptId}`).text(d.copy_count);
}
} catch {}
});
},
// ── Lazy loading ───────────────────────────────────────────────────────────
initLazy() {
if ('IntersectionObserver' in window) {
const obs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
const img = e.target;
img.src = img.dataset.src;
img.classList.add('loaded');
obs.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('img.lazy').forEach(img => obs.observe(img));
} else {
document.querySelectorAll('img.lazy').forEach(img => { img.src = img.dataset.src; img.classList.add('loaded'); });
}
},
// ── Skeleton → Cards ───────────────────────────────────────────────────────
hideSkeleton() {
$('.skeleton-grid').fadeOut(200, function() { $(this).remove(); });
$('#promptGrid').fadeIn(300);
},
// ── Infinite scroll / AJAX search ─────────────────────────────────────────
initSearch() {
let searchTimer;
$('#liveSearch').on('input', function() {
clearTimeout(searchTimer);
const q = $(this).val().trim();
if (q.length < 2) { $('#liveResults').hide(); return; }
searchTimer = setTimeout(() => {
$.get(PV.siteUrl + '/ajax/search.php', { q }, (res) => {
try {
const d = JSON.parse(res);
if (d.results && d.results.length) {
let html = '';
d.results.forEach(p => {
html += `
`;
});
$('#liveResults').html(html).show();
} else {
$('#liveResults').html('No results found
').show();
}
} catch {}
});
}, 400);
});
$(document).on('click', function(e) {
if (!$(e.target).closest('#searchWrapper').length) $('#liveResults').hide();
});
},
// ── Init ──────────────────────────────────────────────────────────────────
init() {
this.initLazy();
this.initSearch();
// Load voices
if ('speechSynthesis' in window) { window.speechSynthesis.onvoiceschanged = () => {}; window.speechSynthesis.getVoices(); }
}
};
$(document).ready(function() {
PV.init();
// Copy button click
$(document).on('click', '.btn-copy-prompt', function() {
const promptId = $(this).data('id');
const promptText = $(this).data('prompt');
PV.copyCountdown(promptId, promptText);
});
// Quick copy (no countdown)
$(document).on('click', '.btn-quick-copy', function() {
const text = $(this).data('text');
navigator.clipboard.writeText(text).then(() => PV.toast('Copied!', 'success'));
});
// Navbar scroll effect
$(window).on('scroll', function() {
if ($(this).scrollTop() > 60) {
$('#mainNav').addClass('scrolled');
} else {
$('#mainNav').removeClass('scrolled');
}
});
// Category filter buttons
$(document).on('click', '.filter-btn[data-filter]', function() {
$('.filter-btn[data-filter]').removeClass('active');
$(this).addClass('active');
const filter = $(this).data('filter');
const url = new URL(window.location.href);
if (filter === 'all') { url.searchParams.delete('sort'); } else { url.searchParams.set('sort', filter); }
url.searchParams.delete('page');
window.location.href = url.toString();
});
// Image zoom on detail page
$('#detailImage').on('click', function() {
$(this).toggleClass('zoomed');
});
// Share buttons
$(document).on('click', '.share-btn', function(e) {
e.preventDefault();
window.open($(this).attr('href'), '_blank', 'width=600,height=400');
});
// Animate cards on scroll
const animObserver = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('fade-up'); animObserver.unobserve(e.target); } });
}, { threshold: 0.1 });
document.querySelectorAll('.prompt-card').forEach(c => animObserver.observe(c));
});
/* ── FAVORITES (localStorage) ────────────────────── */
(function(){
const KEY='pv_favorites';
function get(){try{return JSON.parse(localStorage.getItem(KEY)||'[]');}catch{return[];}}
function save(a){localStorage.setItem(KEY,JSON.stringify(a));if(typeof window.pvUpdateFavBadge==='function')window.pvUpdateFavBadge();}
function has(id){return get().some(f=>f.id==id);}
function syncBtn(btn){
if(!btn)return;
const i=btn.querySelector('i');
if(has(btn.dataset.id)){
btn.style.background='rgba(229,57,53,.2)';
btn.style.borderColor='rgba(229,57,53,.5)';
btn.classList.add('active');
if(i){i.classList.remove('bi-heart');i.classList.add('bi-heart-fill');}
} else {
btn.style.background='rgba(0,0,0,.65)';
btn.style.borderColor='rgba(229,57,53,.3)';
btn.classList.remove('active');
if(i){i.classList.remove('bi-heart-fill');i.classList.add('bi-heart');}
}
}
function init(){
document.querySelectorAll('.btn-fav-prompt').forEach(btn=>{
syncBtn(btn);
if(btn._pvFavBound)return;
btn._pvFavBound=true;
btn.addEventListener('click',function(e){
e.preventDefault();e.stopPropagation();
const id=this.dataset.id;
if(has(id)){
save(get().filter(f=>f.id!=id));
PV.toast('Favorites থেকে সরানো হয়েছে','info');
} else {
const favs=get().filter(f=>f.id!=id);
favs.unshift({id,slug:this.dataset.slug,title:this.dataset.title,
caption:this.dataset.caption||'',ai_model:this.dataset.ai||'',image:this.dataset.image||''});
save(favs);
PV.toast('❤️ Favorites-এ যোগ হয়েছে!','success');
const ic=this.querySelector('i');
if(ic){ic.style.transform='scale(1.5)';setTimeout(()=>ic.style.transform='',280);}
}
document.querySelectorAll('.btn-fav-prompt[data-id="'+id+'"]').forEach(b=>syncBtn(b));
});
});
}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);
else init();
window.pvInitFavBtns=init;
})();