// 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 `
Targets: ${b.channels.join(‘, ‘)}
${statusBadge} ${b.time}
${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();