// Tab Controller
function switchTab(tabId, btn) {
document.querySelectorAll(‘.tab-content’).forEach(el => el.classList.remove(‘active’));
document.querySelectorAll(‘.tab-btn’).forEach(el => el.classList.remove(‘active’));
document.getElementById(tabId).classList.add(‘active’);
btn.classList.add(‘active’);
}
// Core Form Elements & State
const form = document.getElementById(‘broadcast-form’);
const msgInput = document.getElementById(‘bc-message’);
const charCount = document.getElementById(‘char-count’);
const templateSelect = document.getElementById(‘template-select’);
const mediaInput = document.getElementById(‘bc-media’);
const previewWrapper = document.getElementById(‘preview-wrapper’);
const dropPrompt = document.getElementById(‘drop-prompt’);
const btnRemoveMedia = document.getElementById(‘btn-remove-media’);
const feedList = document.getElementById(‘feed-list’);
const modalSnippet = document.getElementById(‘modal-snippet’);
const snippetCodeArea = document.getElementById(‘snippet-code-area’);
const btnRecordAudio = document.getElementById(‘btn-record-audio’);
const recStatus = document.getElementById(‘rec-status’);
let currentMedia = { type: null, src: null };
let broadcasts = JSON.parse(localStorage.getItem(‘bc_v4_history’) || ‘[]’);
let mediaRecorder = null;
let audioChunks = [];
// Character Counter
msgInput.addEventListener(‘input’, () => charCount.textContent = `${msgInput.value.length}/280`);
// Template Dropdown Selection
templateSelect.addEventListener(‘change’, (e) => {
if (e.target.value) {
msgInput.value = e.target.value;
charCount.textContent = `${msgInput.value.length}/280`;
}
});
// Media File Attachment Preview (Images, Videos, Audios)
mediaInput.addEventListener(‘change’, (e) => {
const file = e.target.files[0];
if (!file) return;
processFile(file);
});
function processFile(file) {
const reader = new FileReader();
const mimeType = file.type.split(‘/’)[0];
reader.onload = (evt) => {
// Ensure src is valid base64 string
currentMedia = {
type: mimeType === ‘image’ || mimeType === ‘video’ || mimeType === ‘audio’ ? mimeType : ‘image’,
src: evt.target.result
};
renderMediaPreview(currentMedia.type, currentMedia.src);
};
reader.readAsDataURL(file);
}
function renderMediaPreview(type, src) {
const oldMedia = previewWrapper.querySelector(‘img, video, audio’);
if (oldMedia) oldMedia.remove();
let element;
if (type === ‘video’) {
element = document.createElement(‘video’);
element.controls = true;
} else if (type === ‘audio’) {
element = document.createElement(‘audio’);
element.controls = true;
} else {
element = document.createElement(‘img’);
}
element.src = src;
previewWrapper.appendChild(element);
dropPrompt.style.display = ‘none’;
previewWrapper.style.display = ‘block’;
}
function clearMedia() {
mediaInput.value = ”;
currentMedia = { type: null, src: null };
const oldMedia = previewWrapper.querySelector(‘img, video, audio’);
if (oldMedia) oldMedia.remove();
previewWrapper.style.display = ‘none’;
dropPrompt.style.display = ‘flex’;
}
btnRemoveMedia.addEventListener(‘click’, (e) => {
e.stopPropagation();
clearMedia();
});
// Voice Memo / Audio Recording Integration
btnRecordAudio.addEventListener(‘click’, async () => {
if (mediaRecorder && mediaRecorder.state === “recording”) {
mediaRecorder.stop();
btnRecordAudio.classList.remove(‘recording’);
recStatus.textContent = “Record Audio Transmission”;
return;
}
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: ‘audio/mp3’ });
const reader = new FileReader();
reader.readAsDataURL(audioBlob);
reader.onloadend = () => {
currentMedia = { type: ‘audio’, src: reader.result };
renderMediaPreview(‘audio’, reader.result);
};
};
mediaRecorder.start();
btnRecordAudio.classList.add(‘recording’);
recStatus.textContent = “Recording… Click to Stop”;
} catch (err) {
alert(‘Microphone access refused or unavailable.’);
}
});
// Form Submit / Dispatch Handler
form.addEventListener(‘submit’, (e) => {
e.preventDefault();
const text = msgInput.value.trim();
const channels = Array.from(document.querySelectorAll(‘input[name=”channels”]:checked’)).map(c => c.value);
const scheduleTime = document.getElementById(‘bc-schedule-time’).value;
const isScheduled = scheduleTime && new Date(scheduleTime) > new Date();
// Explicitly grab media state
const payloadMedia = currentMedia.src ? { type: currentMedia.type, src: currentMedia.src } : { type: null, src: null };
const payload = {
id: ‘bc_’ + Date.now(),
time: isScheduled ? new Date(scheduleTime).toLocaleString() : new Date().toLocaleTimeString([], { hour: ‘2-digit’, minute: ‘2-digit’ }),
status: isScheduled ? ‘scheduled’ : ‘sent’,
text: text,
media: payloadMedia,
channels: channels
};
broadcasts.unshift(payload);
// Safely save to LocalStorage with size warning fallback
try {
localStorage.setItem(‘bc_v4_history’, JSON.stringify(broadcasts));
} catch (err) {
alert(“LocalStorage limit reached. Lower the image/video file size.”);
}
renderFeed();
updateMetrics();
// Reset Form and Media State
form.reset();
clearMedia();
charCount.textContent = ‘0/280’;
// Switch to stream view
switchTab(‘tab-console’, document.querySelectorAll(‘.tab-btn’)[1]);
});
function renderFeed() {
if (!broadcasts.length) {
feedList.innerHTML = `
No broadcast log available.
`;
return;
}
feedList.innerHTML = broadcasts.map(b => {
let mediaHTML = ”;
if (b.media && b.media.src) {
if (b.media.type === ‘video’) {
mediaHTML = `
`;
} else if (b.media.type === ‘audio’) {
mediaHTML = `
`;
} else {
mediaHTML = `

`;
}
}
const statusBadge = b.status === ‘scheduled’
? `
Scheduled`
: `
Dispatched`;
return `
${escapeHTML(b.text)}
${mediaHTML}
`;
}).join(”);
}
function updateMetrics() {
const sentCount = broadcasts.filter(b => b.status === ‘sent’).length;
document.getElementById(‘metric-sent’).textContent = sentCount;
document.getElementById(‘metric-reach’).textContent = (sentCount * 284).toLocaleString();
}
window.deleteItem = function(id) {
broadcasts = broadcasts.filter(b => b.id !== id);
try {
localStorage.setItem(‘bc_v4_history’, JSON.stringify(broadcasts));
} catch (e) {}
renderFeed();
updateMetrics();
};
document.getElementById(‘btn-clear-feed’).addEventListener(‘click’, () => {
broadcasts = [];
localStorage.removeItem(‘bc_v4_history’);
renderFeed();
updateMetrics();
});
function escapeHTML(str) { return str.replace(/[&<>'”]/g, tag => ({ ‘&’: ‘&’, ‘<‘: ‘<‘, ‘>’: ‘>’, “‘”: ”’, ‘”‘: ‘”‘ }[tag] || tag)); }
// Initial Execution
renderFeed();
updateMetrics();