|
|
|
|
|
let currentPlatformURL = '';
|
|
|
let currentPlatformName = '';
|
|
|
let currentPlatform = '';
|
|
|
let currentNoteEditPlatform = '';
|
|
|
let timeTrackingInterval = null;
|
|
|
let startTime = null;
|
|
|
let currentImageData = null;
|
|
|
let currentDescription = '';
|
|
|
let currentCaptions = '';
|
|
|
let currentHashtags = '';
|
|
|
|
|
|
|
|
|
const StorageManager = {
|
|
|
|
|
|
getFavorites: () => {
|
|
|
const favorites = localStorage.getItem('socialHub_favorites');
|
|
|
return favorites ? JSON.parse(favorites) : [];
|
|
|
},
|
|
|
|
|
|
|
|
|
setFavorites: (favorites) => {
|
|
|
localStorage.setItem('socialHub_favorites', JSON.stringify(favorites));
|
|
|
},
|
|
|
|
|
|
|
|
|
toggleFavorite: (platform) => {
|
|
|
const favorites = StorageManager.getFavorites();
|
|
|
const index = favorites.indexOf(platform);
|
|
|
if (index > -1) {
|
|
|
favorites.splice(index, 1);
|
|
|
} else {
|
|
|
favorites.push(platform);
|
|
|
}
|
|
|
StorageManager.setFavorites(favorites);
|
|
|
return favorites;
|
|
|
},
|
|
|
|
|
|
|
|
|
getNotes: () => {
|
|
|
const notes = localStorage.getItem('socialHub_notes');
|
|
|
return notes ? JSON.parse(notes) : {};
|
|
|
},
|
|
|
|
|
|
|
|
|
setNote: (platform, note) => {
|
|
|
const notes = StorageManager.getNotes();
|
|
|
if (note.trim()) {
|
|
|
notes[platform] = note;
|
|
|
} else {
|
|
|
delete notes[platform];
|
|
|
}
|
|
|
localStorage.setItem('socialHub_notes', JSON.stringify(notes));
|
|
|
},
|
|
|
|
|
|
|
|
|
getAnalytics: () => {
|
|
|
const analytics = localStorage.getItem('socialHub_analytics');
|
|
|
return analytics ? JSON.parse(analytics) : {};
|
|
|
},
|
|
|
|
|
|
|
|
|
updateAnalytics: (platform) => {
|
|
|
const analytics = StorageManager.getAnalytics();
|
|
|
if (!analytics[platform]) {
|
|
|
analytics[platform] = { visits: 0, time: 0, lastVisit: null };
|
|
|
}
|
|
|
analytics[platform].visits++;
|
|
|
analytics[platform].lastVisit = new Date().toISOString();
|
|
|
localStorage.setItem('socialHub_analytics', JSON.stringify(analytics));
|
|
|
},
|
|
|
|
|
|
|
|
|
updateTimeSpent: (platform, seconds) => {
|
|
|
const analytics = StorageManager.getAnalytics();
|
|
|
if (!analytics[platform]) {
|
|
|
analytics[platform] = { visits: 0, time: 0, lastVisit: null };
|
|
|
}
|
|
|
analytics[platform].time = (analytics[platform].time || 0) + seconds;
|
|
|
localStorage.setItem('socialHub_analytics', JSON.stringify(analytics));
|
|
|
},
|
|
|
|
|
|
|
|
|
getRecentActivity: () => {
|
|
|
const recent = localStorage.getItem('socialHub_recent');
|
|
|
return recent ? JSON.parse(recent) : [];
|
|
|
},
|
|
|
|
|
|
|
|
|
addToRecent: (platform, name, icon) => {
|
|
|
let recent = StorageManager.getRecentActivity();
|
|
|
recent = recent.filter(item => item.platform !== platform);
|
|
|
recent.unshift({ platform, name, icon, timestamp: new Date().toISOString() });
|
|
|
recent = recent.slice(0, 10);
|
|
|
localStorage.setItem('socialHub_recent', JSON.stringify(recent));
|
|
|
},
|
|
|
|
|
|
|
|
|
getTheme: () => {
|
|
|
return localStorage.getItem('socialHub_theme') || 'dark';
|
|
|
},
|
|
|
|
|
|
|
|
|
setTheme: (theme) => {
|
|
|
localStorage.setItem('socialHub_theme', theme);
|
|
|
},
|
|
|
|
|
|
|
|
|
getCustomPlatforms: () => {
|
|
|
const custom = localStorage.getItem('socialHub_custom');
|
|
|
return custom ? JSON.parse(custom) : [];
|
|
|
},
|
|
|
|
|
|
|
|
|
addCustomPlatform: (platform) => {
|
|
|
const custom = StorageManager.getCustomPlatforms();
|
|
|
custom.push(platform);
|
|
|
localStorage.setItem('socialHub_custom', JSON.stringify(custom));
|
|
|
},
|
|
|
|
|
|
|
|
|
removeCustomPlatform: (id) => {
|
|
|
let custom = StorageManager.getCustomPlatforms();
|
|
|
custom = custom.filter(p => p.id !== id);
|
|
|
localStorage.setItem('socialHub_custom', JSON.stringify(custom));
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
const canvas = document.getElementById('particles');
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
|
canvas.width = window.innerWidth;
|
|
|
canvas.height = window.innerHeight;
|
|
|
|
|
|
let particlesArray = [];
|
|
|
const numberOfParticles = 100;
|
|
|
|
|
|
class Particle {
|
|
|
constructor() {
|
|
|
this.x = Math.random() * canvas.width;
|
|
|
this.y = Math.random() * canvas.height;
|
|
|
this.size = Math.random() * 2 + 1;
|
|
|
this.speedX = Math.random() * 1 - 0.5;
|
|
|
this.speedY = Math.random() * 1 - 0.5;
|
|
|
this.opacity = Math.random() * 0.5 + 0.2;
|
|
|
}
|
|
|
|
|
|
update() {
|
|
|
this.x += this.speedX;
|
|
|
this.y += this.speedY;
|
|
|
|
|
|
if (this.x > canvas.width || this.x < 0) {
|
|
|
this.speedX = -this.speedX;
|
|
|
}
|
|
|
if (this.y > canvas.height || this.y < 0) {
|
|
|
this.speedY = -this.speedY;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
draw() {
|
|
|
ctx.fillStyle = `rgba(0, 212, 255, ${this.opacity})`;
|
|
|
ctx.beginPath();
|
|
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
|
|
ctx.fill();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function initParticles() {
|
|
|
particlesArray = [];
|
|
|
for (let i = 0; i < numberOfParticles; i++) {
|
|
|
particlesArray.push(new Particle());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function animateParticles() {
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
for (let i = 0; i < particlesArray.length; i++) {
|
|
|
particlesArray[i].update();
|
|
|
particlesArray[i].draw();
|
|
|
|
|
|
|
|
|
for (let j = i; j < particlesArray.length; j++) {
|
|
|
const dx = particlesArray[i].x - particlesArray[j].x;
|
|
|
const dy = particlesArray[i].y - particlesArray[j].y;
|
|
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
|
|
if (distance < 100) {
|
|
|
ctx.strokeStyle = `rgba(0, 212, 255, ${0.2 * (1 - distance / 100)})`;
|
|
|
ctx.lineWidth = 1;
|
|
|
ctx.beginPath();
|
|
|
ctx.moveTo(particlesArray[i].x, particlesArray[i].y);
|
|
|
ctx.lineTo(particlesArray[j].x, particlesArray[j].y);
|
|
|
ctx.stroke();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
requestAnimationFrame(animateParticles);
|
|
|
}
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
canvas.width = window.innerWidth;
|
|
|
canvas.height = window.innerHeight;
|
|
|
initParticles();
|
|
|
});
|
|
|
|
|
|
|
|
|
const searchInput = document.getElementById('searchInput');
|
|
|
const socialCards = document.querySelectorAll('.social-card');
|
|
|
|
|
|
searchInput.addEventListener('input', (e) => {
|
|
|
const searchTerm = e.target.value.toLowerCase();
|
|
|
const activeFilter = document.querySelector('.filter-tab.active')?.getAttribute('data-filter') || 'all';
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
const platformName = card.querySelector('.platform-name').textContent.toLowerCase();
|
|
|
const platformDesc = card.querySelector('.platform-desc').textContent.toLowerCase();
|
|
|
const category = card.getAttribute('data-category');
|
|
|
const isFavorite = StorageManager.getFavorites().includes(platform);
|
|
|
|
|
|
const matchesSearch = platform.includes(searchTerm) ||
|
|
|
platformName.includes(searchTerm) ||
|
|
|
platformDesc.includes(searchTerm);
|
|
|
|
|
|
const matchesFilter = activeFilter === 'all' ||
|
|
|
(activeFilter === 'favorites' && isFavorite) ||
|
|
|
(activeFilter === 'recent' && isInRecentActivity(platform)) ||
|
|
|
(activeFilter !== 'favorites' && activeFilter !== 'recent' && category === activeFilter);
|
|
|
|
|
|
if (matchesSearch && matchesFilter) {
|
|
|
card.classList.remove('hidden');
|
|
|
} else {
|
|
|
card.classList.add('hidden');
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
function isInRecentActivity(platform) {
|
|
|
const recent = StorageManager.getRecentActivity();
|
|
|
return recent.some(item => item.platform === platform);
|
|
|
}
|
|
|
|
|
|
|
|
|
const filterTabs = document.querySelectorAll('.filter-tab');
|
|
|
const recentTimeline = document.getElementById('recentTimeline');
|
|
|
|
|
|
filterTabs.forEach(tab => {
|
|
|
tab.addEventListener('click', () => {
|
|
|
|
|
|
filterTabs.forEach(t => t.classList.remove('active'));
|
|
|
tab.classList.add('active');
|
|
|
|
|
|
const filter = tab.getAttribute('data-filter');
|
|
|
|
|
|
|
|
|
if (filter === 'recent') {
|
|
|
updateRecentTimeline();
|
|
|
recentTimeline.style.display = 'block';
|
|
|
} else {
|
|
|
recentTimeline.style.display = 'none';
|
|
|
}
|
|
|
|
|
|
|
|
|
filterCards(filter);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
function filterCards(filter) {
|
|
|
const favorites = StorageManager.getFavorites();
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
const category = card.getAttribute('data-category');
|
|
|
const searchTerm = searchInput.value.toLowerCase();
|
|
|
const platformName = card.querySelector('.platform-name').textContent.toLowerCase();
|
|
|
const platformDesc = card.querySelector('.platform-desc').textContent.toLowerCase();
|
|
|
|
|
|
const matchesSearch = !searchTerm || platform.includes(searchTerm) ||
|
|
|
platformName.includes(searchTerm) || platformDesc.includes(searchTerm);
|
|
|
|
|
|
let show = false;
|
|
|
|
|
|
if (filter === 'all') {
|
|
|
show = true;
|
|
|
} else if (filter === 'favorites') {
|
|
|
show = favorites.includes(platform);
|
|
|
} else if (filter === 'recent') {
|
|
|
show = isInRecentActivity(platform);
|
|
|
} else {
|
|
|
show = category === filter;
|
|
|
}
|
|
|
|
|
|
if (show && matchesSearch) {
|
|
|
card.classList.remove('hidden');
|
|
|
} else {
|
|
|
card.classList.add('hidden');
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeFavorites() {
|
|
|
const favorites = StorageManager.getFavorites();
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
const favBtn = card.querySelector('.favorite-btn');
|
|
|
|
|
|
if (favorites.includes(platform)) {
|
|
|
favBtn.classList.add('active');
|
|
|
favBtn.querySelector('i').classList.remove('far');
|
|
|
favBtn.querySelector('i').classList.add('fas');
|
|
|
}
|
|
|
|
|
|
favBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
|
e.preventDefault();
|
|
|
toggleFavorite(platform, favBtn);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function toggleFavorite(platform, btn) {
|
|
|
const favorites = StorageManager.toggleFavorite(platform);
|
|
|
const isFavorite = favorites.includes(platform);
|
|
|
|
|
|
if (isFavorite) {
|
|
|
btn.classList.add('active');
|
|
|
btn.querySelector('i').classList.remove('far');
|
|
|
btn.querySelector('i').classList.add('fas');
|
|
|
showNotification(`Added to favorites!`, 'success');
|
|
|
} else {
|
|
|
btn.classList.remove('active');
|
|
|
btn.querySelector('i').classList.remove('fas');
|
|
|
btn.querySelector('i').classList.add('far');
|
|
|
showNotification(`Removed from favorites`, 'info');
|
|
|
}
|
|
|
|
|
|
updateAnalyticsDisplay();
|
|
|
}
|
|
|
|
|
|
|
|
|
const noteModal = document.getElementById('noteModal');
|
|
|
const noteTextarea = document.getElementById('noteTextarea');
|
|
|
const notePlatformName = document.getElementById('notePlatformName');
|
|
|
const saveNoteBtn = document.getElementById('saveNoteBtn');
|
|
|
const deleteNoteBtn = document.getElementById('deleteNoteBtn');
|
|
|
|
|
|
function initializeNotes() {
|
|
|
const notes = StorageManager.getNotes();
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
const noteBtn = card.querySelector('.note-btn');
|
|
|
const noteIndicator = card.querySelector('.note-indicator');
|
|
|
|
|
|
if (notes[platform]) {
|
|
|
noteBtn.classList.add('has-note');
|
|
|
noteIndicator.style.display = 'inline-flex';
|
|
|
}
|
|
|
|
|
|
noteBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
|
e.preventDefault();
|
|
|
openNoteModal(platform, card.querySelector('.platform-name').textContent);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function openNoteModal(platform, name) {
|
|
|
currentNoteEditPlatform = platform;
|
|
|
notePlatformName.textContent = name;
|
|
|
|
|
|
const notes = StorageManager.getNotes();
|
|
|
noteTextarea.value = notes[platform] || '';
|
|
|
|
|
|
noteModal.classList.add('active');
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
noteTextarea.focus();
|
|
|
}
|
|
|
|
|
|
saveNoteBtn.addEventListener('click', () => {
|
|
|
const note = noteTextarea.value;
|
|
|
StorageManager.setNote(currentNoteEditPlatform, note);
|
|
|
|
|
|
|
|
|
const card = document.querySelector(`[data-platform="${currentNoteEditPlatform}"]`);
|
|
|
const noteBtn = card.querySelector('.note-btn');
|
|
|
const noteIndicator = card.querySelector('.note-indicator');
|
|
|
|
|
|
if (note.trim()) {
|
|
|
noteBtn.classList.add('has-note');
|
|
|
noteIndicator.style.display = 'inline-flex';
|
|
|
showNotification('Note saved!', 'success');
|
|
|
} else {
|
|
|
noteBtn.classList.remove('has-note');
|
|
|
noteIndicator.style.display = 'none';
|
|
|
showNotification('Note deleted', 'info');
|
|
|
}
|
|
|
|
|
|
noteModal.classList.remove('active');
|
|
|
document.body.style.overflow = '';
|
|
|
});
|
|
|
|
|
|
deleteNoteBtn.addEventListener('click', () => {
|
|
|
noteTextarea.value = '';
|
|
|
saveNoteBtn.click();
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const platformURLs = {
|
|
|
facebook: 'https://www.facebook.com',
|
|
|
instagram: 'https://www.instagram.com',
|
|
|
twitter: 'https://www.twitter.com',
|
|
|
linkedin: 'https://www.linkedin.com',
|
|
|
youtube: 'https://www.youtube.com',
|
|
|
tiktok: 'https://www.tiktok.com',
|
|
|
reddit: 'https://www.reddit.com',
|
|
|
whatsapp: 'https://web.whatsapp.com',
|
|
|
telegram: 'https://web.telegram.org',
|
|
|
discord: 'https://discord.com/app',
|
|
|
snapchat: 'https://www.snapchat.com',
|
|
|
pinterest: 'https://www.pinterest.com'
|
|
|
};
|
|
|
|
|
|
|
|
|
const platformIcons = {
|
|
|
facebook: 'fab fa-facebook-f',
|
|
|
instagram: 'fab fa-instagram',
|
|
|
twitter: 'fab fa-twitter',
|
|
|
linkedin: 'fab fa-linkedin-in',
|
|
|
youtube: 'fab fa-youtube',
|
|
|
tiktok: 'fab fa-tiktok',
|
|
|
reddit: 'fab fa-reddit-alien',
|
|
|
whatsapp: 'fab fa-whatsapp',
|
|
|
telegram: 'fab fa-telegram-plane',
|
|
|
discord: 'fab fa-discord',
|
|
|
snapchat: 'fab fa-snapchat-ghost',
|
|
|
pinterest: 'fab fa-pinterest-p'
|
|
|
};
|
|
|
|
|
|
|
|
|
const modal = document.getElementById('platformModal');
|
|
|
const modalFrame = document.getElementById('platformFrame');
|
|
|
const loadingSpinner = document.querySelector('.loading-spinner');
|
|
|
const closeBtn = document.querySelector('.close-btn');
|
|
|
const platformIcon = document.querySelector('.platform-icon');
|
|
|
const platformTitle = document.querySelector('.platform-title');
|
|
|
const modalContent = document.querySelector('.modal-content');
|
|
|
|
|
|
|
|
|
const authSection = document.getElementById('authSection');
|
|
|
const platformSection = document.getElementById('platformSection');
|
|
|
const googleSignInBtn = document.getElementById('googleSignIn');
|
|
|
const platformSignInBtn = document.getElementById('platformSignIn');
|
|
|
const guestAccessBtn = document.getElementById('guestAccess');
|
|
|
const refreshBtn = document.querySelector('.refresh-btn');
|
|
|
const fullscreenBtn = document.querySelector('.fullscreen-btn');
|
|
|
const logoutBtn = document.querySelector('.logout-btn');
|
|
|
const platformIconLarge = document.querySelector('.platform-icon-large');
|
|
|
const platformIconSmall = document.querySelector('.platform-icon-small');
|
|
|
const platformNameText = document.querySelector('.platform-name-text');
|
|
|
const platformLoginText = document.querySelector('.platform-login-text');
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const launchBtn = card.querySelector('.launch-btn');
|
|
|
|
|
|
launchBtn.addEventListener('click', (e) => {
|
|
|
e.preventDefault();
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
const platformName = card.querySelector('.platform-name').textContent;
|
|
|
const url = platformURLs[platform];
|
|
|
const icon = platformIcons[platform];
|
|
|
|
|
|
console.log(`Opening ${platform} in modal`);
|
|
|
|
|
|
|
|
|
StorageManager.updateAnalytics(platform);
|
|
|
StorageManager.addToRecent(platform, platformName, icon);
|
|
|
updateVisitBadge(card, platform);
|
|
|
updateAnalyticsDisplay();
|
|
|
|
|
|
|
|
|
openPlatformModal(url, platformName, platform);
|
|
|
|
|
|
|
|
|
card.style.transform = 'scale(0.95)';
|
|
|
setTimeout(() => {
|
|
|
card.style.transform = '';
|
|
|
}, 200);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
function openPlatformModal(url, name, platform) {
|
|
|
currentPlatformURL = url;
|
|
|
currentPlatformName = name;
|
|
|
currentPlatform = platform;
|
|
|
|
|
|
|
|
|
platformTitle.textContent = name;
|
|
|
platformIcon.className = 'platform-icon ' + platformIcons[platform];
|
|
|
|
|
|
|
|
|
platformNameText.textContent = name;
|
|
|
platformIconLarge.className = 'platform-icon-large ' + platformIcons[platform];
|
|
|
platformIconSmall.className = 'platform-icon-small ' + platformIcons[platform];
|
|
|
platformLoginText.textContent = `Login with ${name} Account`;
|
|
|
|
|
|
|
|
|
modal.classList.add('active');
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
|
|
|
|
|
authSection.style.display = 'flex';
|
|
|
platformSection.style.display = 'none';
|
|
|
}
|
|
|
|
|
|
|
|
|
function closePlatformModal() {
|
|
|
modal.classList.remove('active');
|
|
|
document.body.style.overflow = '';
|
|
|
modalContent.classList.remove('fullscreen');
|
|
|
|
|
|
|
|
|
stopTimeTracking();
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
modalFrame.src = '';
|
|
|
authSection.style.display = 'flex';
|
|
|
platformSection.style.display = 'none';
|
|
|
loadingSpinner.classList.remove('hidden');
|
|
|
}, 300);
|
|
|
}
|
|
|
|
|
|
|
|
|
function loadPlatform(authMethod) {
|
|
|
console.log(`Loading ${currentPlatformName} with ${authMethod} authentication`);
|
|
|
|
|
|
|
|
|
authSection.style.display = 'none';
|
|
|
platformSection.style.display = 'block';
|
|
|
|
|
|
|
|
|
loadingSpinner.classList.remove('hidden');
|
|
|
|
|
|
|
|
|
modalFrame.src = currentPlatformURL;
|
|
|
|
|
|
|
|
|
modalFrame.onload = () => {
|
|
|
setTimeout(() => {
|
|
|
loadingSpinner.classList.add('hidden');
|
|
|
}, 500);
|
|
|
};
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
loadingSpinner.classList.add('hidden');
|
|
|
}, 3000);
|
|
|
}
|
|
|
|
|
|
|
|
|
googleSignInBtn.addEventListener('click', () => {
|
|
|
console.log('Google Sign-In clicked');
|
|
|
showNotification('Google Sign-In feature coming soon! Opening platform directly...', 'info');
|
|
|
|
|
|
|
|
|
const width = 1200;
|
|
|
const height = 800;
|
|
|
const left = (screen.width - width) / 2;
|
|
|
const top = (screen.height - height) / 2;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
window.open(
|
|
|
currentPlatformURL,
|
|
|
currentPlatformName,
|
|
|
`width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,scrollbars=yes,resizable=yes`
|
|
|
);
|
|
|
|
|
|
closePlatformModal();
|
|
|
}, 1000);
|
|
|
});
|
|
|
|
|
|
|
|
|
platformSignInBtn.addEventListener('click', () => {
|
|
|
console.log('Platform Sign-In clicked');
|
|
|
showNotification(`Opening ${currentPlatformName} in new window...`, 'success');
|
|
|
|
|
|
|
|
|
startTimeTracking(currentPlatform);
|
|
|
|
|
|
|
|
|
const width = 1200;
|
|
|
const height = 800;
|
|
|
const left = (screen.width - width) / 2;
|
|
|
const top = (screen.height - height) / 2;
|
|
|
|
|
|
window.open(
|
|
|
currentPlatformURL,
|
|
|
currentPlatformName,
|
|
|
`width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,scrollbars=yes,resizable=yes`
|
|
|
);
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
closePlatformModal();
|
|
|
}, 500);
|
|
|
});
|
|
|
|
|
|
|
|
|
guestAccessBtn.addEventListener('click', () => {
|
|
|
console.log('Guest access clicked');
|
|
|
showNotification(`Opening ${currentPlatformName} web version...`, 'info');
|
|
|
|
|
|
|
|
|
const width = 1200;
|
|
|
const height = 800;
|
|
|
const left = (screen.width - width) / 2;
|
|
|
const top = (screen.height - height) / 2;
|
|
|
|
|
|
window.open(
|
|
|
currentPlatformURL,
|
|
|
currentPlatformName + '_Web',
|
|
|
`width=${width},height=${height},left=${left},top=${top},toolbar=yes,menubar=no,scrollbars=yes,resizable=yes,location=yes`
|
|
|
);
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
closePlatformModal();
|
|
|
}, 500);
|
|
|
});
|
|
|
|
|
|
|
|
|
function showNotification(message, type = 'info') {
|
|
|
const notification = document.createElement('div');
|
|
|
notification.className = `notification notification-${type}`;
|
|
|
notification.innerHTML = `
|
|
|
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'}"></i>
|
|
|
<span>${message}</span>
|
|
|
`;
|
|
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
|
|
|
|
|
setTimeout(() => notification.classList.add('show'), 10);
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
notification.classList.remove('show');
|
|
|
setTimeout(() => notification.remove(), 300);
|
|
|
}, 3000);
|
|
|
}
|
|
|
|
|
|
|
|
|
closeBtn.addEventListener('click', closePlatformModal);
|
|
|
|
|
|
|
|
|
modal.addEventListener('click', (e) => {
|
|
|
if (e.target === modal) {
|
|
|
closePlatformModal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
refreshBtn.addEventListener('click', () => {
|
|
|
loadingSpinner.classList.remove('hidden');
|
|
|
modalFrame.src = modalFrame.src;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
loadingSpinner.classList.add('hidden');
|
|
|
}, 2000);
|
|
|
});
|
|
|
|
|
|
|
|
|
fullscreenBtn.addEventListener('click', () => {
|
|
|
modalContent.classList.toggle('fullscreen');
|
|
|
const icon = fullscreenBtn.querySelector('i');
|
|
|
|
|
|
if (modalContent.classList.contains('fullscreen')) {
|
|
|
icon.classList.remove('fa-expand');
|
|
|
icon.classList.add('fa-compress');
|
|
|
fullscreenBtn.title = 'Exit Fullscreen';
|
|
|
} else {
|
|
|
icon.classList.remove('fa-compress');
|
|
|
icon.classList.add('fa-expand');
|
|
|
fullscreenBtn.title = 'Fullscreen';
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
logoutBtn.addEventListener('click', () => {
|
|
|
showNotification('Logged out successfully', 'success');
|
|
|
authSection.style.display = 'flex';
|
|
|
platformSection.style.display = 'none';
|
|
|
modalFrame.src = '';
|
|
|
});
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
|
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
|
|
closePlatformModal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
card.addEventListener('mouseenter', () => {
|
|
|
card.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
|
anchor.addEventListener('click', function (e) {
|
|
|
e.preventDefault();
|
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
|
if (target) {
|
|
|
target.scrollIntoView({
|
|
|
behavior: 'smooth',
|
|
|
block: 'start'
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
function updateGreeting() {
|
|
|
const hour = new Date().getHours();
|
|
|
const subtitle = document.querySelector('.subtitle');
|
|
|
|
|
|
if (hour >= 5 && hour < 12) {
|
|
|
subtitle.textContent = 'Good morning! Connect to your favorite platforms';
|
|
|
} else if (hour >= 12 && hour < 17) {
|
|
|
subtitle.textContent = 'Good afternoon! Connect to your favorite platforms';
|
|
|
} else if (hour >= 17 && hour < 21) {
|
|
|
subtitle.textContent = 'Good evening! Connect to your favorite platforms';
|
|
|
} else {
|
|
|
subtitle.textContent = 'Connect to your favorite platforms instantly';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
window.addEventListener('load', () => {
|
|
|
document.body.style.opacity = '0';
|
|
|
setTimeout(() => {
|
|
|
document.body.style.transition = 'opacity 0.5s ease';
|
|
|
document.body.style.opacity = '1';
|
|
|
}, 100);
|
|
|
});
|
|
|
|
|
|
|
|
|
initParticles();
|
|
|
animateParticles();
|
|
|
updateGreeting();
|
|
|
initializeFavorites();
|
|
|
initializeNotes();
|
|
|
initializeAnalytics();
|
|
|
initializeTheme();
|
|
|
initializeCustomPlatforms();
|
|
|
initializeStatusIndicators();
|
|
|
initializeKeyboardShortcuts();
|
|
|
initializeHeaderControls();
|
|
|
|
|
|
|
|
|
function initializeAnalytics() {
|
|
|
const analytics = StorageManager.getAnalytics();
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
const platform = card.getAttribute('data-platform');
|
|
|
updateVisitBadge(card, platform);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function updateVisitBadge(card, platform) {
|
|
|
const analytics = StorageManager.getAnalytics();
|
|
|
const visitBadge = card.querySelector('.visit-badge');
|
|
|
|
|
|
if (analytics[platform] && analytics[platform].visits > 0) {
|
|
|
visitBadge.textContent = `${analytics[platform].visits} visits`;
|
|
|
visitBadge.style.display = 'inline-flex';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function updateAnalyticsDisplay() {
|
|
|
const analytics = StorageManager.getAnalytics();
|
|
|
const favorites = StorageManager.getFavorites();
|
|
|
|
|
|
let totalVisits = 0;
|
|
|
let totalTime = 0;
|
|
|
let mostUsedPlatform = '-';
|
|
|
let maxVisits = 0;
|
|
|
|
|
|
Object.keys(analytics).forEach(platform => {
|
|
|
totalVisits += analytics[platform].visits || 0;
|
|
|
totalTime += analytics[platform].time || 0;
|
|
|
|
|
|
if (analytics[platform].visits > maxVisits) {
|
|
|
maxVisits = analytics[platform].visits;
|
|
|
mostUsedPlatform = platform;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
document.getElementById('totalVisits').textContent = totalVisits;
|
|
|
document.getElementById('totalFavorites').textContent = favorites.length;
|
|
|
|
|
|
const hours = Math.floor(totalTime / 3600);
|
|
|
const minutes = Math.floor((totalTime % 3600) / 60);
|
|
|
document.getElementById('totalTime').textContent = `${hours}h ${minutes}m`;
|
|
|
|
|
|
if (mostUsedPlatform !== '-') {
|
|
|
const card = document.querySelector(`[data-platform="${mostUsedPlatform}"]`);
|
|
|
if (card) {
|
|
|
mostUsedPlatform = card.querySelector('.platform-name').textContent;
|
|
|
}
|
|
|
}
|
|
|
document.getElementById('mostUsed').textContent = mostUsedPlatform;
|
|
|
|
|
|
updateUsageChart(analytics);
|
|
|
}
|
|
|
|
|
|
function updateUsageChart(analytics) {
|
|
|
const chartContainer = document.getElementById('usageChart');
|
|
|
chartContainer.innerHTML = '';
|
|
|
|
|
|
const sorted = Object.entries(analytics)
|
|
|
.sort((a, b) => b[1].visits - a[1].visits)
|
|
|
.slice(0, 10);
|
|
|
|
|
|
if (sorted.length === 0) {
|
|
|
chartContainer.innerHTML = '<p style="color: var(--text-secondary); text-align: center;">No usage data yet</p>';
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const maxVisits = sorted[0][1].visits;
|
|
|
|
|
|
sorted.forEach(([platform, data]) => {
|
|
|
const card = document.querySelector(`[data-platform="${platform}"]`);
|
|
|
const name = card ? card.querySelector('.platform-name').textContent : platform;
|
|
|
const percentage = (data.visits / maxVisits) * 100;
|
|
|
|
|
|
const barHTML = `
|
|
|
<div class="chart-bar">
|
|
|
<div class="chart-label">${name}</div>
|
|
|
<div class="chart-bar-container">
|
|
|
<div class="chart-bar-fill" style="width: ${percentage}%">
|
|
|
${data.visits}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
chartContainer.innerHTML += barHTML;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeTheme() {
|
|
|
const theme = StorageManager.getTheme();
|
|
|
applyTheme(theme);
|
|
|
}
|
|
|
|
|
|
function applyTheme(theme) {
|
|
|
const themeToggle = document.getElementById('themeToggle');
|
|
|
const icon = themeToggle.querySelector('i');
|
|
|
|
|
|
if (theme === 'light') {
|
|
|
document.body.classList.add('light-mode');
|
|
|
icon.classList.remove('fa-moon');
|
|
|
icon.classList.add('fa-sun');
|
|
|
} else {
|
|
|
document.body.classList.remove('light-mode');
|
|
|
icon.classList.remove('fa-sun');
|
|
|
icon.classList.add('fa-moon');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
function updateRecentTimeline() {
|
|
|
const recent = StorageManager.getRecentActivity();
|
|
|
const timelineItems = document.getElementById('timelineItems');
|
|
|
|
|
|
if (recent.length === 0) {
|
|
|
timelineItems.innerHTML = '<p style="color: var(--text-secondary); text-align: center;">No recent activity</p>';
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
timelineItems.innerHTML = '';
|
|
|
|
|
|
recent.forEach(item => {
|
|
|
const timeAgo = getTimeAgo(new Date(item.timestamp));
|
|
|
const itemHTML = `
|
|
|
<div class="timeline-item" data-platform="${item.platform}">
|
|
|
<i class="${item.icon}"></i>
|
|
|
<div class="timeline-info">
|
|
|
<h4>${item.name}</h4>
|
|
|
<span>${timeAgo}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
timelineItems.innerHTML += itemHTML;
|
|
|
});
|
|
|
|
|
|
|
|
|
timelineItems.querySelectorAll('.timeline-item').forEach(item => {
|
|
|
item.addEventListener('click', () => {
|
|
|
const platform = item.getAttribute('data-platform');
|
|
|
const card = document.querySelector(`[data-platform="${platform}"]`);
|
|
|
if (card) {
|
|
|
card.querySelector('.launch-btn').click();
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function getTimeAgo(date) {
|
|
|
const seconds = Math.floor((new Date() - date) / 1000);
|
|
|
|
|
|
if (seconds < 60) return 'Just now';
|
|
|
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
|
|
|
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
|
|
|
return `${Math.floor(seconds / 86400)} days ago`;
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeCustomPlatforms() {
|
|
|
const customPlatforms = StorageManager.getCustomPlatforms();
|
|
|
const socialGrid = document.getElementById('socialGrid');
|
|
|
|
|
|
customPlatforms.forEach(platform => {
|
|
|
addCustomPlatformCard(platform, socialGrid);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function addCustomPlatformCard(platform, container) {
|
|
|
const cardHTML = `
|
|
|
<div class="social-card" data-platform="${platform.id}" data-category="${platform.category}" data-custom="true">
|
|
|
<div class="card-inner">
|
|
|
<div class="card-front" style="background: ${platform.color};">
|
|
|
<div class="card-top-controls">
|
|
|
<button class="favorite-btn" title="Add to Favorites">
|
|
|
<i class="far fa-star"></i>
|
|
|
</button>
|
|
|
<button class="note-btn" title="Add Note">
|
|
|
<i class="far fa-sticky-note"></i>
|
|
|
</button>
|
|
|
<button class="delete-custom-btn" title="Delete Platform" style="background: rgba(255, 71, 87, 0.8);">
|
|
|
<i class="fas fa-trash"></i>
|
|
|
</button>
|
|
|
<div class="status-indicator" title="Status: Online"></div>
|
|
|
</div>
|
|
|
<div class="platform-logo">
|
|
|
<i class="${platform.icon}"></i>
|
|
|
</div>
|
|
|
<h3 class="platform-name">${platform.name}</h3>
|
|
|
<div class="platform-badges">
|
|
|
<span class="visit-badge" style="display: none;">0 visits</span>
|
|
|
<span class="note-indicator" style="display: none;"><i class="fas fa-sticky-note"></i></span>
|
|
|
</div>
|
|
|
<p class="platform-desc">Custom platform</p>
|
|
|
<div class="card-stats">
|
|
|
<span><i class="fas fa-globe"></i> Custom</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<a href="${platform.url}" target="_blank" class="launch-btn" data-url="${platform.url}">
|
|
|
<i class="fas fa-external-link-alt"></i> Launch
|
|
|
</a>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
container.innerHTML += cardHTML;
|
|
|
|
|
|
|
|
|
const newCard = container.lastElementChild;
|
|
|
initializeCardFeatures(newCard, platform.id);
|
|
|
}
|
|
|
|
|
|
function initializeCardFeatures(card, platform) {
|
|
|
|
|
|
const favBtn = card.querySelector('.favorite-btn');
|
|
|
const favorites = StorageManager.getFavorites();
|
|
|
if (favorites.includes(platform)) {
|
|
|
favBtn.classList.add('active');
|
|
|
favBtn.querySelector('i').classList.remove('far');
|
|
|
favBtn.querySelector('i').classList.add('fas');
|
|
|
}
|
|
|
favBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
|
e.preventDefault();
|
|
|
toggleFavorite(platform, favBtn);
|
|
|
});
|
|
|
|
|
|
|
|
|
const noteBtn = card.querySelector('.note-btn');
|
|
|
const notes = StorageManager.getNotes();
|
|
|
if (notes[platform]) {
|
|
|
noteBtn.classList.add('has-note');
|
|
|
card.querySelector('.note-indicator').style.display = 'inline-flex';
|
|
|
}
|
|
|
noteBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
|
e.preventDefault();
|
|
|
openNoteModal(platform, card.querySelector('.platform-name').textContent);
|
|
|
});
|
|
|
|
|
|
|
|
|
const deleteBtn = card.querySelector('.delete-custom-btn');
|
|
|
if (deleteBtn) {
|
|
|
deleteBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
|
e.preventDefault();
|
|
|
if (confirm('Delete this custom platform?')) {
|
|
|
StorageManager.removeCustomPlatform(platform);
|
|
|
card.remove();
|
|
|
showNotification('Platform deleted', 'success');
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
const launchBtn = card.querySelector('.launch-btn');
|
|
|
launchBtn.addEventListener('click', (e) => {
|
|
|
e.preventDefault();
|
|
|
const platformName = card.querySelector('.platform-name').textContent;
|
|
|
const url = launchBtn.getAttribute('data-url');
|
|
|
const icon = card.querySelector('.platform-logo i').className;
|
|
|
|
|
|
StorageManager.updateAnalytics(platform);
|
|
|
StorageManager.addToRecent(platform, platformName, icon);
|
|
|
updateVisitBadge(card, platform);
|
|
|
updateAnalyticsDisplay();
|
|
|
|
|
|
openPlatformModal(url, platformName, platform);
|
|
|
|
|
|
card.style.transform = 'scale(0.95)';
|
|
|
setTimeout(() => {
|
|
|
card.style.transform = '';
|
|
|
}, 200);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeStatusIndicators() {
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.status-indicator').forEach(indicator => {
|
|
|
indicator.classList.remove('offline');
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeKeyboardShortcuts() {
|
|
|
document.addEventListener('keydown', (e) => {
|
|
|
|
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (e.key === '?') {
|
|
|
e.preventDefault();
|
|
|
document.getElementById('shortcutsModal').classList.add('active');
|
|
|
}
|
|
|
|
|
|
|
|
|
if (e.ctrlKey && e.key === 'k') {
|
|
|
e.preventDefault();
|
|
|
document.getElementById('analyticsModal').classList.add('active');
|
|
|
updateAnalyticsDisplay();
|
|
|
}
|
|
|
|
|
|
|
|
|
if (e.ctrlKey && e.key === 'd') {
|
|
|
e.preventDefault();
|
|
|
document.getElementById('themeToggle').click();
|
|
|
}
|
|
|
|
|
|
|
|
|
if (e.ctrlKey && e.key === 'i') {
|
|
|
e.preventDefault();
|
|
|
document.getElementById('aiCaptionBtn').click();
|
|
|
}
|
|
|
|
|
|
|
|
|
if (e.key >= '1' && e.key <= '9') {
|
|
|
const index = parseInt(e.key) - 1;
|
|
|
const visibleCards = Array.from(socialCards).filter(card => !card.classList.contains('hidden'));
|
|
|
if (visibleCards[index]) {
|
|
|
visibleCards[index].querySelector('.launch-btn').click();
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
function initializeHeaderControls() {
|
|
|
|
|
|
document.getElementById('themeToggle').addEventListener('click', () => {
|
|
|
const currentTheme = StorageManager.getTheme();
|
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
|
StorageManager.setTheme(newTheme);
|
|
|
applyTheme(newTheme);
|
|
|
showNotification(`Switched to ${newTheme} mode`, 'success');
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('shortcutsBtn').addEventListener('click', () => {
|
|
|
document.getElementById('shortcutsModal').classList.add('active');
|
|
|
});
|
|
|
|
|
|
|
|
|
document.getElementById('analyticsBtn').addEventListener('click', () => {
|
|
|
document.getElementById('analyticsModal').classList.add('active');
|
|
|
updateAnalyticsDisplay();
|
|
|
});
|
|
|
|
|
|
|
|
|
document.getElementById('addPlatformBtn').addEventListener('click', () => {
|
|
|
document.getElementById('addPlatformModal').classList.add('active');
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
document.getElementById('addPlatformForm').addEventListener('submit', (e) => {
|
|
|
e.preventDefault();
|
|
|
|
|
|
const platform = {
|
|
|
id: 'custom_' + Date.now(),
|
|
|
name: document.getElementById('customPlatformName').value,
|
|
|
url: document.getElementById('customPlatformURL').value,
|
|
|
icon: document.getElementById('customPlatformIcon').value,
|
|
|
color: document.getElementById('customPlatformColor').value,
|
|
|
category: document.getElementById('customPlatformCategory').value
|
|
|
};
|
|
|
|
|
|
StorageManager.addCustomPlatform(platform);
|
|
|
addCustomPlatformCard(platform, document.getElementById('socialGrid'));
|
|
|
|
|
|
document.getElementById('addPlatformModal').classList.remove('active');
|
|
|
document.getElementById('addPlatformForm').reset();
|
|
|
showNotification('Custom platform added!', 'success');
|
|
|
});
|
|
|
|
|
|
|
|
|
function startTimeTracking(platform) {
|
|
|
startTime = Date.now();
|
|
|
const tracker = document.getElementById('timeTracker');
|
|
|
tracker.style.display = 'flex';
|
|
|
|
|
|
timeTrackingInterval = setInterval(() => {
|
|
|
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
|
const minutes = Math.floor(elapsed / 60);
|
|
|
const seconds = elapsed % 60;
|
|
|
document.getElementById('trackerTime').textContent =
|
|
|
`${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
|
|
}, 1000);
|
|
|
}
|
|
|
|
|
|
function stopTimeTracking() {
|
|
|
if (timeTrackingInterval) {
|
|
|
clearInterval(timeTrackingInterval);
|
|
|
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
|
StorageManager.updateTimeSpent(currentPlatform, elapsed);
|
|
|
document.getElementById('timeTracker').style.display = 'none';
|
|
|
updateAnalyticsDisplay();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
document.getElementById('stopTracking').addEventListener('click', stopTimeTracking);
|
|
|
|
|
|
|
|
|
|
|
|
const GROQ_API_KEY = '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';
|
|
|
const GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions';
|
|
|
const HF_IMAGE_API = 'https://api-inference.huggingface.co/models/Salesforce/blip-image-captioning-base';
|
|
|
const HF_API_TOKEN = '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';
|
|
|
|
|
|
|
|
|
document.getElementById('aiCaptionBtn').addEventListener('click', () => {
|
|
|
document.getElementById('aiCaptionModal').classList.add('active');
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
});
|
|
|
|
|
|
|
|
|
const imageInput = document.getElementById('imageInput');
|
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
|
const uploadSection = document.getElementById('uploadSection');
|
|
|
const resultsSection = document.getElementById('resultsSection');
|
|
|
const imagePreview = document.getElementById('imagePreview');
|
|
|
|
|
|
imageInput.addEventListener('change', handleImageUpload);
|
|
|
|
|
|
|
|
|
uploadArea.addEventListener('dragover', (e) => {
|
|
|
e.preventDefault();
|
|
|
uploadArea.style.borderColor = 'var(--accent-color)';
|
|
|
uploadArea.style.background = 'rgba(0, 212, 255, 0.15)';
|
|
|
});
|
|
|
|
|
|
uploadArea.addEventListener('dragleave', () => {
|
|
|
uploadArea.style.borderColor = 'rgba(0, 212, 255, 0.3)';
|
|
|
uploadArea.style.background = 'rgba(0, 212, 255, 0.05)';
|
|
|
});
|
|
|
|
|
|
uploadArea.addEventListener('drop', (e) => {
|
|
|
e.preventDefault();
|
|
|
uploadArea.style.borderColor = 'rgba(0, 212, 255, 0.3)';
|
|
|
uploadArea.style.background = 'rgba(0, 212, 255, 0.05)';
|
|
|
|
|
|
const files = e.dataTransfer.files;
|
|
|
if (files.length > 0 && files[0].type.startsWith('image/')) {
|
|
|
imageInput.files = files;
|
|
|
handleImageUpload();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
async function handleImageUpload() {
|
|
|
const file = imageInput.files[0];
|
|
|
if (!file) return;
|
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = (e) => {
|
|
|
imagePreview.src = e.target.result;
|
|
|
currentImageData = e.target.result;
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
|
|
|
|
uploadSection.style.display = 'none';
|
|
|
resultsSection.style.display = 'grid';
|
|
|
|
|
|
|
|
|
showLoadingState();
|
|
|
|
|
|
|
|
|
await generateAIContent(file);
|
|
|
}
|
|
|
|
|
|
function showLoadingState() {
|
|
|
document.getElementById('aiDescription').innerHTML = '<div class="loading-dots"><span></span><span></span><span></span></div>';
|
|
|
document.getElementById('aiCaptions').innerHTML = '<div class="loading-dots"><span></span><span></span><span></span></div>';
|
|
|
document.getElementById('aiHashtags').innerHTML = '<div class="loading-dots"><span></span><span></span><span></span></div>';
|
|
|
}
|
|
|
|
|
|
async function generateAIContent(imageFile) {
|
|
|
try {
|
|
|
console.log('=== Starting AI Generation ===');
|
|
|
console.log('Image file:', imageFile.name, imageFile.type, imageFile.size, 'bytes');
|
|
|
|
|
|
|
|
|
showNotification('Analyzing image with AI...', 'info');
|
|
|
const description = await getImageDescription(imageFile);
|
|
|
currentDescription = description;
|
|
|
|
|
|
console.log('✓ Image description:', description);
|
|
|
document.getElementById('aiDescription').innerHTML = `<p>${description}</p>`;
|
|
|
|
|
|
|
|
|
showNotification('Generating creative captions...', 'info');
|
|
|
const captions = await generateCaptions(description);
|
|
|
currentCaptions = captions;
|
|
|
|
|
|
console.log('✓ Captions generated:', captions);
|
|
|
document.getElementById('aiCaptions').innerHTML = formatCaptions(captions);
|
|
|
|
|
|
|
|
|
showNotification('Creating hashtags...', 'info');
|
|
|
const hashtags = await generateHashtags(description);
|
|
|
currentHashtags = hashtags;
|
|
|
|
|
|
console.log('✓ Hashtags created:', hashtags);
|
|
|
document.getElementById('aiHashtags').innerHTML = `<p>${hashtags}</p>`;
|
|
|
|
|
|
console.log('=== AI Generation Complete ===');
|
|
|
showNotification('AI generation complete! ✨', 'success');
|
|
|
} catch (error) {
|
|
|
console.error('=== AI Generation Error ===');
|
|
|
console.error('Error:', error);
|
|
|
showNotification('AI generation failed. Using fallback content...', 'warning');
|
|
|
useFallbackGeneration();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function getImageDescription(imageFile) {
|
|
|
try {
|
|
|
|
|
|
const imageData = await imageFile.arrayBuffer();
|
|
|
const blob = new Blob([imageData]);
|
|
|
|
|
|
const response = await fetch(HF_IMAGE_API, {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Authorization': `Bearer ${HF_API_TOKEN}`,
|
|
|
},
|
|
|
body: blob
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
const errorText = await response.text();
|
|
|
console.error('HF API Error:', errorText);
|
|
|
|
|
|
|
|
|
if (response.status === 503) {
|
|
|
showNotification('AI model is loading, retrying in 3 seconds...', 'info');
|
|
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
|
return getImageDescription(imageFile);
|
|
|
}
|
|
|
|
|
|
throw new Error('Image API request failed');
|
|
|
}
|
|
|
|
|
|
const result = await response.json();
|
|
|
console.log('Image description result:', result);
|
|
|
|
|
|
|
|
|
const description = result[0]?.generated_text || result?.generated_text || 'an interesting image';
|
|
|
return description.trim();
|
|
|
} catch (error) {
|
|
|
console.error('Image description error:', error);
|
|
|
showNotification('Using basic image analysis...', 'warning');
|
|
|
return 'a photo with interesting elements';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function generateCaptions(description) {
|
|
|
const prompt = `Generate exactly 3 unique, creative, and engaging social media captions for a photo that shows: "${description}". Make them catchy and suitable for Instagram, Facebook, or Twitter. Format as:
|
|
|
1. [first caption]
|
|
|
2. [second caption]
|
|
|
3. [third caption]
|
|
|
|
|
|
Only provide the numbered captions, nothing else.`;
|
|
|
|
|
|
try {
|
|
|
console.log('Generating captions for:', description);
|
|
|
|
|
|
const response = await fetch(GROQ_API_URL, {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Authorization': `Bearer ${GROQ_API_KEY}`,
|
|
|
'Content-Type': 'application/json'
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
model: 'llama-3.1-8b-instant',
|
|
|
messages: [
|
|
|
{
|
|
|
role: 'system',
|
|
|
content: 'You are a creative social media caption writer. Generate engaging, concise captions.'
|
|
|
},
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: prompt
|
|
|
}
|
|
|
],
|
|
|
max_tokens: 200,
|
|
|
temperature: 0.8
|
|
|
})
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
const errorText = await response.text();
|
|
|
console.error('Groq API Error:', errorText);
|
|
|
throw new Error('Caption API request failed');
|
|
|
}
|
|
|
|
|
|
const result = await response.json();
|
|
|
console.log('Caption result:', result);
|
|
|
|
|
|
const generatedText = result.choices[0]?.message?.content || '';
|
|
|
return generatedText.trim() || getFallbackCaptions(description);
|
|
|
} catch (error) {
|
|
|
console.error('Caption generation error:', error);
|
|
|
showNotification('Using fallback captions...', 'warning');
|
|
|
return getFallbackCaptions(description);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function generateHashtags(description) {
|
|
|
const prompt = `Generate exactly 10 relevant and trending hashtags for a photo that shows: "${description}". Only provide the hashtags with # symbol, separated by spaces. No explanations.`;
|
|
|
|
|
|
try {
|
|
|
console.log('Generating hashtags for:', description);
|
|
|
|
|
|
const response = await fetch(GROQ_API_URL, {
|
|
|
method: 'POST',
|
|
|
headers: {
|
|
|
'Authorization': `Bearer ${GROQ_API_KEY}`,
|
|
|
'Content-Type': 'application/json'
|
|
|
},
|
|
|
body: JSON.stringify({
|
|
|
model: 'llama-3.1-8b-instant',
|
|
|
messages: [
|
|
|
{
|
|
|
role: 'system',
|
|
|
content: 'You are a social media expert. Generate relevant hashtags based on image descriptions.'
|
|
|
},
|
|
|
{
|
|
|
role: 'user',
|
|
|
content: prompt
|
|
|
}
|
|
|
],
|
|
|
max_tokens: 100,
|
|
|
temperature: 0.7
|
|
|
})
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
const errorText = await response.text();
|
|
|
console.error('Hashtag API Error:', errorText);
|
|
|
throw new Error('Hashtag API request failed');
|
|
|
}
|
|
|
|
|
|
const result = await response.json();
|
|
|
console.log('Hashtag result:', result);
|
|
|
|
|
|
let hashtags = result.choices[0]?.message?.content || '';
|
|
|
|
|
|
|
|
|
hashtags = hashtags.split(/\s+/)
|
|
|
.map(tag => tag.trim())
|
|
|
.filter(tag => tag.length > 0)
|
|
|
.map(tag => tag.startsWith('#') ? tag : '#' + tag)
|
|
|
.slice(0, 10)
|
|
|
.join(' ');
|
|
|
|
|
|
return hashtags || getFallbackHashtags(description);
|
|
|
} catch (error) {
|
|
|
console.error('Hashtag generation error:', error);
|
|
|
return getFallbackHashtags(description);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function getFallbackHashtags(description) {
|
|
|
|
|
|
const words = description.split(' ')
|
|
|
.filter(w => w.length > 3)
|
|
|
.map(w => w.replace(/[^a-zA-Z]/g, ''))
|
|
|
.filter(w => w.length > 0);
|
|
|
|
|
|
const baseHashtags = words.slice(0, 5).map(w => '#' + w.toLowerCase());
|
|
|
|
|
|
const genericHashtags = [
|
|
|
'#photography', '#photooftheday', '#instagood',
|
|
|
'#beautiful', '#instadaily', '#picoftheday',
|
|
|
'#instagram', '#love', '#amazing', '#art'
|
|
|
];
|
|
|
|
|
|
const allHashtags = [...new Set([...baseHashtags, ...genericHashtags])].slice(0, 10);
|
|
|
return allHashtags.join(' ');
|
|
|
}
|
|
|
|
|
|
function getFallbackCaptions(description) {
|
|
|
|
|
|
const mainSubject = description.split(' ').slice(0, 3).join(' ');
|
|
|
|
|
|
return `1. Capturing the beauty of ${description} 📸✨
|
|
|
|
|
|
2. ${description.charAt(0).toUpperCase() + description.slice(1)} - moments like these make life special 🌟
|
|
|
|
|
|
3. Just ${mainSubject}... living my best life! 💫 #blessed`;
|
|
|
}
|
|
|
|
|
|
function formatCaptions(captions) {
|
|
|
|
|
|
const captionArray = captions.split(/\d+\./).filter(c => c.trim());
|
|
|
|
|
|
let html = '';
|
|
|
captionArray.forEach((caption, index) => {
|
|
|
if (caption.trim()) {
|
|
|
html += `<p><strong>Caption ${index + 1}:</strong> ${caption.trim()}</p>`;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
return html || `<p>${captions}</p>`;
|
|
|
}
|
|
|
|
|
|
function useFallbackGeneration() {
|
|
|
currentDescription = 'a wonderful moment captured in time';
|
|
|
currentCaptions = getFallbackCaptions(currentDescription);
|
|
|
currentHashtags = '#photography #photooftheday #instagood #beautiful #instadaily #picoftheday #instagram #love #amazing #art';
|
|
|
|
|
|
document.getElementById('aiDescription').innerHTML = `<p>${currentDescription}</p>`;
|
|
|
document.getElementById('aiCaptions').innerHTML = formatCaptions(currentCaptions);
|
|
|
document.getElementById('aiHashtags').innerHTML = `<p>${currentHashtags}</p>`;
|
|
|
}
|
|
|
|
|
|
|
|
|
document.getElementById('copyCaptionBtn').addEventListener('click', () => {
|
|
|
copyToClipboard(currentCaptions, 'Caption copied!');
|
|
|
});
|
|
|
|
|
|
document.getElementById('copyHashtagsBtn').addEventListener('click', () => {
|
|
|
copyToClipboard(currentHashtags, 'Hashtags copied!');
|
|
|
});
|
|
|
|
|
|
document.getElementById('copyAllBtn').addEventListener('click', () => {
|
|
|
const allText = `${currentCaptions}\n\n${currentHashtags}`;
|
|
|
copyToClipboard(allText, 'All content copied!');
|
|
|
});
|
|
|
|
|
|
function copyToClipboard(text, message) {
|
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
|
showNotification(message, 'success');
|
|
|
}).catch(() => {
|
|
|
showNotification('Copy failed', 'warning');
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
document.querySelectorAll('.platform-post-btn').forEach(btn => {
|
|
|
btn.addEventListener('click', () => {
|
|
|
const platform = btn.getAttribute('data-platform');
|
|
|
postToPlatform(platform);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
function postToPlatform(platform) {
|
|
|
|
|
|
const fullContent = `${currentCaptions.split('\n')[0]}\n\n${currentHashtags}`;
|
|
|
copyToClipboard(fullContent, `Content copied! Opening ${platform}...`);
|
|
|
|
|
|
|
|
|
const platformURL = platformURLs[platform];
|
|
|
if (platformURL) {
|
|
|
setTimeout(() => {
|
|
|
const width = 1200;
|
|
|
const height = 800;
|
|
|
const left = (screen.width - width) / 2;
|
|
|
const top = (screen.height - height) / 2;
|
|
|
|
|
|
window.open(
|
|
|
platformURL,
|
|
|
platform,
|
|
|
`width=${width},height=${height},left=${left},top=${top},toolbar=yes,menubar=no,scrollbars=yes,resizable=yes`
|
|
|
);
|
|
|
|
|
|
showNotification(`Paste your caption and upload the image on ${platform}!`, 'info');
|
|
|
}, 500);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let ticking = false;
|
|
|
window.addEventListener('scroll', () => {
|
|
|
if (!ticking) {
|
|
|
window.requestAnimationFrame(() => {
|
|
|
|
|
|
ticking = false;
|
|
|
});
|
|
|
ticking = true;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
|
|
|
|
if (modal.classList.contains('active') && e.key !== 'Escape') {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (e.key === '/') {
|
|
|
e.preventDefault();
|
|
|
searchInput.focus();
|
|
|
}
|
|
|
if (e.key === 'Escape') {
|
|
|
if (modal.classList.contains('active')) {
|
|
|
closePlatformModal();
|
|
|
} else {
|
|
|
searchInput.blur();
|
|
|
searchInput.value = '';
|
|
|
searchInput.dispatchEvent(new Event('input'));
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
const searchContainer = document.querySelector('.search-container');
|
|
|
const hintElement = document.createElement('div');
|
|
|
hintElement.style.cssText = `
|
|
|
position: absolute;
|
|
|
right: 4rem;
|
|
|
top: 50%;
|
|
|
transform: translateY(-50%);
|
|
|
font-size: 0.8rem;
|
|
|
color: var(--text-secondary);
|
|
|
opacity: 0.6;
|
|
|
pointer-events: none;
|
|
|
`;
|
|
|
hintElement.innerHTML = '<kbd style="background: rgba(0,212,255,0.1); padding: 2px 6px; border-radius: 3px; font-size: 0.7rem;">/ to search</kbd>';
|
|
|
searchContainer.appendChild(hintElement);
|
|
|
|
|
|
searchInput.addEventListener('focus', () => {
|
|
|
hintElement.style.opacity = '0';
|
|
|
});
|
|
|
|
|
|
searchInput.addEventListener('blur', () => {
|
|
|
if (!searchInput.value) {
|
|
|
hintElement.style.opacity = '0.6';
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
socialCards.forEach(card => {
|
|
|
card.addEventListener('click', function(e) {
|
|
|
const ripple = document.createElement('div');
|
|
|
const rect = this.getBoundingClientRect();
|
|
|
const size = Math.max(rect.width, rect.height);
|
|
|
const x = e.clientX - rect.left - size / 2;
|
|
|
const y = e.clientY - rect.top - size / 2;
|
|
|
|
|
|
ripple.style.cssText = `
|
|
|
position: absolute;
|
|
|
width: ${size}px;
|
|
|
height: ${size}px;
|
|
|
border-radius: 50%;
|
|
|
background: rgba(255, 255, 255, 0.3);
|
|
|
left: ${x}px;
|
|
|
top: ${y}px;
|
|
|
pointer-events: none;
|
|
|
animation: rippleEffect 0.6s ease-out;
|
|
|
`;
|
|
|
|
|
|
this.style.position = 'relative';
|
|
|
this.style.overflow = 'hidden';
|
|
|
this.appendChild(ripple);
|
|
|
|
|
|
setTimeout(() => ripple.remove(), 600);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
const style = document.createElement('style');
|
|
|
style.textContent = `
|
|
|
@keyframes rippleEffect {
|
|
|
0% {
|
|
|
transform: scale(0);
|
|
|
opacity: 1;
|
|
|
}
|
|
|
100% {
|
|
|
transform: scale(2);
|
|
|
opacity: 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
kbd {
|
|
|
font-family: 'Courier New', monospace;
|
|
|
}
|
|
|
`;
|
|
|
document.head.appendChild(style);
|
|
|
|
|
|
console.log('%c🚀 Social Hub Loaded Successfully!', 'color: #00d4ff; font-size: 20px; font-weight: bold;');
|
|
|
console.log('%cPress "/" to search platforms', 'color: #00ff88; font-size: 14px;');
|
|
|
console.log('%cPress "Ctrl+I" to open AI Caption Generator', 'color: #00ff88; font-size: 14px;');
|
|
|
console.log('%c🤖 AI Features: Real-time image analysis with Groq + Hugging Face', 'color: #ffa502; font-size: 12px;');
|
|
|
|