var url = `https://api.giphy.com/v1/gifs/search?api_key=${apiKey}&q=${encodeURIComponent(query)}&limit=30&rating=g`;/* Beep — Twitter-style microblog frontend */ (function () { 'use strict'; var config = window.BeepConfig || {}; var limit = (config.charLimit || 280) | 0; document.addEventListener('DOMContentLoaded', init); function init() { var feeds = document.querySelectorAll('[data-beep-feed]'); Array.prototype.forEach.call(feeds, initFeed); } function initFeed(feed) { var compose = feed.querySelector('[data-beep-compose]'); if (compose) initCompose(compose, feed); initSortTabs(feed); initMedia(compose, feed); feed.addEventListener('click', onClick); feed.addEventListener('input', onInput); feed.addEventListener('keydown', onKeydown); } /* ---------- Composer ---------- */ function initCompose(compose, feed) { var input = compose.querySelector('[data-beep-input]'); var counter = compose.querySelector('[data-beep-counter]'); var button = compose.querySelector('[data-beep-submit]'); if (!input || !button) return; function refresh() { autosize(input); var remaining = limit - countChars(input.value); counter.textContent = remaining; counter.classList.toggle('beep-counter-warn', remaining < 20 && remaining >= 0); counter.classList.toggle('beep-counter-bad', remaining < 0); button.disabled = input.value.trim() === '' || remaining < 0; } input.addEventListener('input', refresh); refresh(); button.addEventListener('click', function () { var content = input.value.trim(); if (!content) return; button.disabled = true; var quotedPost = compose._beepQuotedPost; var gifUrl = compose._beepGifUrl; var payload = { content: content }; if (quotedPost && quotedPost.id) payload.quote_id = quotedPost.id; if (gifUrl) payload.gif_url = gifUrl; fetch(config.restUrl + 'post', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: JSON.stringify(payload) }) .then(handleResponse) .then(function (data) { var list = feed.querySelector('[data-beep-list]'); var empty = list.querySelector('.beep-empty'); if (empty) empty.remove(); var tmp = document.createElement('div'); tmp.innerHTML = data.html; var node = tmp.firstElementChild; if (node) { node.classList.add('beep-new'); list.insertBefore(node, list.firstChild); } input.value = ''; clearQuotePreview(compose); if (compose._beepGifUrl) { compose._beepGifUrl = null; var gifPrev = compose.querySelector('[data-beep-gif-preview]'); if (gifPrev) { gifPrev.innerHTML = ''; gifPrev.hidden = true; } } refresh(); // Upload pending images and voice note to this new beep var pending = compose._beepPendingImages; var pendingVoice = compose._beepPendingVoice; var uploadDone = function () { reloadFeed(feed, 'recent'); }; var tasks = []; if (pending && pending.length && data.id) { tasks.push(uploadPendingImages(data.id, pending, compose)); } if (pendingVoice && data.id) { tasks.push(uploadPendingVoice(data.id, pendingVoice, compose)); } // Create poll if poll data exists if (data.id) { tasks.push(createPoll(data.id, compose).then(function() { clearPoll(compose); })); } if (tasks.length) { Promise.all(tasks).then(uploadDone); } else { // No uploads, just clear poll if present clearPoll(compose); } }) .catch(function () { window.alert('Could not post. Please try again.'); button.disabled = false; }); }); } /* ---------- Sort tabs ---------- */ function initSortTabs(feed) { var tabs = feed.querySelector('[data-beep-tabs]'); if (!tabs) return; tabs.addEventListener('click', function (e) { var btn = e.target.closest('[data-beep-sort]'); if (!btn) return; e.preventDefault(); var sort = btn.dataset.beepSort; tabs.querySelectorAll('[data-beep-sort]').forEach(function (b) { b.classList.toggle('is-active', b === btn); }); reloadFeed(feed, sort); }); } function reloadFeed(feed, sort) { var list = feed.querySelector('[data-beep-list]'); if (!list) return; list.classList.add('beep-loading'); fetch(config.restUrl + 'feed?sort=' + encodeURIComponent(sort), { credentials: 'same-origin', headers: { 'X-WP-Nonce': config.nonce } }) .then(handleResponse) .then(function (data) { list.innerHTML = data.html || '
No beeps yet.
'; list.classList.remove('beep-loading'); }) .catch(function () { list.classList.remove('beep-loading'); }); } /* ---------- Click delegation ---------- */ function onClick(e) { var t = e.target; var likePost = t.closest('[data-beep-like-post]'); if (likePost) { e.preventDefault(); if (requireLogin()) handleLike(likePost, 'like/' + likePost.dataset.beepLikePost); return; } var likeReply = t.closest('[data-beep-like-reply]'); if (likeReply) { e.preventDefault(); if (requireLogin()) handleLike(likeReply, 'reply-like/' + likeReply.dataset.beepLikeReply); return; } var replyToggle = t.closest('[data-beep-toggle-reply]'); if (replyToggle) { e.preventDefault(); if (!requireLogin()) return; // The reply form is the sibling .beep-reply-form belonging to the post or reply // that this toggle button is inside. var container = replyToggle.closest('[data-beep-post], [data-beep-reply]'); if (!container) return; var form = container.querySelector(':scope > .beep-post-body > [data-beep-reply-form], :scope > .beep-reply-body > [data-beep-reply-form]'); if (!form) return; form.hidden = !form.hidden; if (!form.hidden) { var ta = form.querySelector('textarea'); if (ta) ta.focus(); } return; } var replyBtn = t.closest('[data-beep-reply-submit]'); if (replyBtn) { e.preventDefault(); handleReplySubmit(replyBtn); return; } var shareBtn = t.closest('[data-beep-share]'); if (shareBtn) { e.preventDefault(); handleShare(shareBtn); return; } var delPost = t.closest('[data-beep-delete]'); if (delPost) { e.preventDefault(); if (!window.confirm('Delete this beep?')) return; handleDelete('beep/' + delPost.dataset.beepDelete, '[data-beep-post="' + delPost.dataset.beepDelete + '"]'); return; } var delReply = t.closest('[data-beep-delete-reply]'); if (delReply) { e.preventDefault(); if (!window.confirm('Delete this reply?')) return; handleDelete('reply/' + delReply.dataset.beepDeleteReply, '[data-beep-reply="' + delReply.dataset.beepDeleteReply + '"]'); return; } // Thread / conversation view var threadBtn = t.closest('[data-beep-thread]'); if (threadBtn) { e.preventDefault(); openThread(threadBtn.dataset.beepThread); return; } // Thread modal close var threadClose = t.closest('[data-beep-thread-close]'); if (threadClose) { e.preventDefault(); closeThread(); return; } // Quote button - open composer with quoted beep pre-loaded var quoteBtn = t.closest('[data-beep-quote]'); if (quoteBtn) { e.preventDefault(); openComposerWithQuote(quoteBtn.dataset.beepQuote); return; } // Quote composer tab toggle var quoteComposerBtn = t.closest('[data-beep-quote-composer-btn]'); if (quoteComposerBtn) { e.preventDefault(); var compose = quoteComposerBtn.closest('[data-beep-compose]'); var quoteSection = compose && compose.querySelector('[data-beep-compose-quote]'); if (quoteSection) quoteSection.hidden = !quoteSection.hidden; return; } // Quote lookup - Add button var quoteLookupBtn = t.closest('[data-beep-quote-lookup-btn]'); if (quoteLookupBtn) { e.preventDefault(); var compose = quoteLookupBtn.closest('[data-beep-compose]'); var input = compose && compose.querySelector('[data-beep-quote-lookup]'); var query = input ? input.value.trim() : ''; if (!query) return; fetch(config.restUrl + 'lookup?q=' + encodeURIComponent(query), { headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function(quoted) { if (compose) { compose._beepQuotedPost = { id: quoted.id, data: quoted }; showQuotePreview(compose, quoted); } }) .catch(function() { window.alert('Beep not found.'); }); return; } // Quote remove button var quoteRemoveBtn = t.closest('[data-beep-quote-remove]'); if (quoteRemoveBtn) { e.preventDefault(); var compose = quoteRemoveBtn.closest('[data-beep-compose]'); clearQuotePreview(compose); return; } // GIF picker button var gifBtn = t.closest('[data-beep-gif-btn]'); if (gifBtn) { e.preventDefault(); var compose = gifBtn.closest('[data-beep-compose]'); openGifPicker(compose); return; } // GIF modal close var gifClose = t.closest('[data-beep-gif-close]'); if (gifClose) { e.preventDefault(); closeGifPicker(); return; } // GIF search on Enter var gifSearchInput = t.closest('[data-beep-gif-search]'); if (gifSearchInput && e.key === 'Enter') { e.preventDefault(); var compose = gifSearchInput.closest('[data-beep-compose]') || document.querySelector('[data-beep-compose]'); searchGifs(gifSearchInput.value, compose); return; } // GIF search button var gifSearchBtn = t.closest('[data-beep-gif-search-btn]'); if (gifSearchBtn) { e.preventDefault(); var compose = gifSearchBtn.closest('[data-beep-compose]') || document.querySelector('[data-beep-compose]'); var input = gifSearchBtn.closest('.beep-gif-search-bar') && gifSearchBtn.closest('.beep-gif-search-bar').querySelector('[data-beep-gif-search]'); if (input) searchGifs(input.value, compose); return; } // GIF remove var gifRemoveBtn = t.closest('[data-beep-gif-remove]'); if (gifRemoveBtn) { e.preventDefault(); var compose = gifRemoveBtn.closest('[data-beep-compose]'); if (compose) { compose._beepGifUrl = null; var preview = compose.querySelector('[data-beep-gif-preview]'); if (preview) { preview.innerHTML = ''; preview.hidden = true; } } return; } // Poll button - toggle poll section var pollBtn = t.closest('[data-beep-poll-btn]'); if (pollBtn) { e.preventDefault(); var compose = pollBtn.closest('[data-beep-compose]'); if (compose) openPollSection(compose); return; } // Poll add option var pollAddOptionBtn = t.closest('[data-beep-poll-add-option]'); if (pollAddOptionBtn) { e.preventDefault(); var compose = pollAddOptionBtn.closest('[data-beep-compose]'); if (compose) addPollOption(compose); return; } // Poll remove option var pollRemoveOptionBtn = t.closest('[data-beep-poll-remove-option]'); if (pollRemoveOptionBtn) { e.preventDefault(); var compose = pollRemoveOptionBtn.closest('[data-beep-compose]'); if (compose) removePollOption(compose); return; } // Poll remove var pollRemoveBtn = t.closest('[data-beep-poll-remove]'); if (pollRemoveBtn) { e.preventDefault(); var compose = pollRemoveBtn.closest('[data-beep-compose]'); if (compose) clearPoll(compose); return; } // Poll vote button var pollVoteBtn = t.closest('[data-beep-poll-vote]'); if (pollVoteBtn) { e.preventDefault(); if (requireLogin()) handlePollVote(pollVoteBtn); return; } } function onInput(e) { var input = e.target.closest('.beep-reply-input, .beep-compose-input'); if (!input) return; autosize(input); var counter = input.parentElement.querySelector('.beep-char-counter'); var submit = input.parentElement.querySelector('.beep-button'); if (counter) { var remaining = limit - countChars(input.value); counter.textContent = remaining; counter.classList.toggle('beep-counter-warn', remaining < 20 && remaining >= 0); counter.classList.toggle('beep-counter-bad', remaining < 0); if (submit) submit.disabled = input.value.trim() === '' || remaining < 0; } } function onKeydown(e) { if (!((e.metaKey || e.ctrlKey) && e.key === 'Enter')) return; var input = e.target.closest('textarea'); if (!input) return; var wrap = input.closest('.beep-compose, .beep-reply-form'); if (!wrap) return; var btn = wrap.querySelector('button'); if (btn && !btn.disabled) btn.click(); } /* ---------- Like (works for both posts and replies) ---------- */ function handleLike(btn, urlFragment) { var countEl = btn.querySelector('[data-beep-like-count]'); var was = btn.classList.contains('is-liked'); btn.classList.toggle('is-liked'); if (!was) { btn.classList.add('beep-just-liked'); setTimeout(function () { btn.classList.remove('beep-just-liked'); }, 600); } if (countEl) { var current = parseInt(countEl.textContent, 10) || 0; var optimistic = current + (was ? -1 : 1); countEl.textContent = optimistic > 0 ? optimistic : ''; } fetch(config.restUrl + urlFragment, { method: 'POST', headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function (data) { if (countEl) countEl.textContent = data.count > 0 ? data.count : ''; btn.classList.toggle('is-liked', !!data.liked); }) .catch(function () { btn.classList.toggle('is-liked', was); }); } /* ---------- Reply submit (top-level or nested) ---------- */ function handleReplySubmit(btn) { var postId = btn.dataset.beepReplySubmit; var parent = btn.dataset.beepReplyParent || '0'; var form = btn.closest('[data-beep-reply-form]'); var input = form && form.querySelector('textarea'); if (!input) return; var content = input.value.trim(); if (!content) return; btn.disabled = true; fetch(config.restUrl + 'reply/' + postId, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: JSON.stringify({ content: content, parent: parseInt(parent, 10) || 0 }) }) .then(handleResponse) .then(function (data) { if (data.pending) { form.innerHTML = '
Reply submitted, awaiting moderation.
'; return; } // Where do we insert? // If parent === 0, append to the post's top-level [data-beep-replies] list. // Otherwise append (creating if needed) to the parent reply's nested list. var post = btn.closest('[data-beep-post]'); var targetList; if ((parseInt(parent, 10) || 0) === 0) { targetList = post && post.querySelector(':scope > .beep-post-body > [data-beep-replies]'); } else { var parentReply = post && post.querySelector('[data-beep-reply="' + parent + '"]'); if (parentReply) { targetList = parentReply.querySelector(':scope > .beep-reply-body > .beep-replies-nested'); if (!targetList) { targetList = document.createElement('div'); targetList.className = 'beep-replies beep-replies-nested'; targetList.setAttribute('data-beep-replies', ''); parentReply.querySelector('.beep-reply-body').appendChild(targetList); } } } if (targetList) { var tmp = document.createElement('div'); tmp.innerHTML = data.html; var node = tmp.firstElementChild; if (node) { node.classList.add('beep-new'); targetList.appendChild(node); } } // Increment top-level reply counter on the post. var countEl = post && post.querySelector('[data-beep-reply-count]'); if (countEl) { countEl.textContent = (parseInt(countEl.textContent, 10) || 0) + 1; } input.value = ''; autosize(input); form.hidden = true; }) .catch(function () { window.alert('Could not post reply. Please try again.'); }) .finally(function () { btn.disabled = false; }); } /* ---------- Share (copy link) ---------- */ function handleShare(btn) { var url = btn.dataset.beepShare; if (!url || !navigator.clipboard) return; navigator.clipboard.writeText(url).then(function () { var tip = document.createElement('span'); tip.className = 'beep-tip'; tip.textContent = 'Copied'; btn.style.position = 'relative'; btn.appendChild(tip); setTimeout(function () { tip.remove(); }, 1500); }); } /* ---------- Delete ---------- */ function handleDelete(pathFragment, selector) { fetch(config.restUrl + pathFragment, { method: 'DELETE', headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function () { var el = document.querySelector(selector); if (el) el.remove(); }) .catch(function () { window.alert('Could not delete.'); }); } /* ---------- Helpers ---------- */ function requireLogin() { if (config.isLoggedIn) return true; if (config.loginUrl) window.location.href = config.loginUrl; return false; } function autosize(el) { el.style.height = 'auto'; el.style.height = el.scrollHeight + 'px'; } function countChars(s) { return Array.from(s).length; } /* ---------- Media upload (images) ---------- */ function initMedia(compose, feed) { var input = compose.querySelector('[data-beep-media-input]'); var preview = compose.querySelector('[data-beep-media-preview]'); var counter = compose.querySelector('[data-beep-media-count]'); var voiceInput = compose.querySelector('[data-beep-voice-input]'); var voicePreview = compose.querySelector('[data-beep-voice-preview]'); if (!input && !voiceInput) return; var pendingImages = []; var pendingVoice = null; // { file, id } // Voice note input if (voiceInput) { voiceInput.addEventListener('change', function (e) { var files = e.target.files; if (!files || !files.length) return; var file = files[0]; if (file.size > 20 * 1024 * 1024) { window.alert('Voice note must be under 20 MB.'); voiceInput.value = ''; return; } var accepted = ['audio/mpeg','audio/ogg','audio/wav','audio/webm','audio/mp4','audio/x-m4a']; if (accepted.indexOf(file.type) === -1) { window.alert('Only MP3, OGG, WAV, WebM, and M4A audio are allowed.'); voiceInput.value = ''; return; } if (pendingVoice) { window.alert('Only one voice note per beep.'); voiceInput.value = ''; return; } var reader = new FileReader(); reader.onload = function (ev) { var id = 'voice-' + Date.now(); pendingVoice = { file: file, id: id }; voicePreview.innerHTML = '
Voice note
'; voicePreview.hidden = false; }; reader.readAsDataURL(file); voiceInput.value = ''; }); } // Voice remove button if (voicePreview) { voicePreview.addEventListener('click', function (e) { var btn = e.target.closest('[data-beep-voice-remove]'); if (!btn) return; pendingVoice = null; voicePreview.innerHTML = ''; voicePreview.hidden = true; }); } input.addEventListener('change', function (e) { var files = Array.prototype.slice.call(e.target.files); if (!files.length) return; var room = 4 - pendingImages.length; if (room <= 0) { input.value = ''; return; } files = files.slice(0, room); files.forEach(function (file) { if (file.size > 10 * 1024 * 1024) { window.alert('Image must be under 10 MB: ' + file.name); return; } var reader = new FileReader(); reader.onload = function (ev) { var url = ev.target.result; var id = 'pending-' + Date.now() + '-' + Math.random().toString(36).substr(2, 6); pendingImages.push({ file: file, previewUrl: url, id: id }); renderPreviews(); }; reader.readAsDataURL(file); }); input.value = ''; }); function renderPreviews() { if (!pendingImages.length) { preview.innerHTML = ''; preview.hidden = true; if (counter) counter.hidden = true; return; } preview.hidden = false; if (counter) { counter.textContent = pendingImages.length + '/4'; counter.hidden = false; } preview.innerHTML = pendingImages.map(function (img) { return '
' + '' + '' + '
'; }).join(''); } preview.addEventListener('click', function (e) { var btn = e.target.closest('[data-remove]'); if (!btn) return; var id = btn.dataset.remove; pendingImages = pendingImages.filter(function (img) { return img.id !== id; }); renderPreviews(); }); compose._beepPendingImages = pendingImages; compose._beepPendingVoice = pendingVoice; } function uploadPendingImages(postId, pendingImages, compose) { if (!pendingImages || !pendingImages.length) return Promise.resolve(); var promises = pendingImages.map(function (img) { var formData = new FormData(); formData.append('file', img.file); return fetch(config.restUrl + 'media/' + postId, { method: 'POST', headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: formData }) .then(handleResponse) .catch(function (err) { console.warn('Image upload failed:', img.file.name, err); }); }); return Promise.all(promises).then(function () { pendingImages.length = 0; if (compose) { var preview = compose.querySelector('[data-beep-media-preview]'); if (preview) { preview.innerHTML = ''; preview.hidden = true; } var counter = compose.querySelector('[data-beep-media-count]'); if (counter) counter.hidden = true; } }); } function uploadPendingVoice(postId, pendingVoice, compose) { if (!pendingVoice) return Promise.resolve(); var formData = new FormData(); formData.append('file', pendingVoice.file); return fetch(config.restUrl + 'voice/' + postId, { method: 'POST', headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: formData }) .then(handleResponse) .then(function () { pendingVoice = null; if (compose) { var vp = compose.querySelector('[data-beep-voice-preview]'); if (vp) { vp.innerHTML = ''; vp.hidden = true; } } }) .catch(function (err) { console.warn('Voice upload failed:', err); }); } function handleResponse(res) { if (!res.ok) return res.json().then(function (j) { throw j; }, function () { throw res; }); return res.json(); } })(); /* ---------- Thread / Conversation modal ---------- */ function openThread(postId) { var modal = document.querySelector('[data-beep-thread-modal]'); var content = modal && modal.querySelector('[data-beep-thread-content]'); var loading = modal && modal.querySelector('.beep-thread-loading'); if (!modal || !content) return; modal.hidden = false; if (loading) loading.style.display = 'block'; content.innerHTML = ''; fetch(config.restUrl + 'thread/' + postId, { headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function(thread) { if (loading) loading.style.display = 'none'; renderThread(thread, content); }) .catch(function() { if (loading) loading.textContent = 'Failed to load conversation.'; }); } function closeThread() { var modal = document.querySelector('[data-beep-thread-modal]'); if (modal) modal.hidden = true; } function renderThread(thread, container) { if (!thread || !thread.length) { container.innerHTML = '

No replies yet.

'; return; } var html = ''; thread.forEach(function(item) { var indent = Math.min(item.depth, 5) * 24; var author = item.author || {}; var avatar = author.avatar || 'https://www.gravatar.com/avatar/?s=48&d=mp'; var replyClass = item.depth > 0 ? 'beep-thread-reply' : 'beep-thread-root'; html += '
'; html += '
' + (author.name || '') + '
'; html += '
'; html += '
'; html += '' + (author.name || 'Unknown') + ''; html += '@' + (author.username || '') + ''; html += '' + (item.time_ago || '') + ''; html += '
'; html += '
' + (item.content || '') + '
'; html += '
'; html += '' + (item.reply_count || 0) + ' replies'; html += '' + (item.like_count || 0) + ' likes'; html += '
'; }); container.innerHTML = html; } /* ---------- Quote posts ---------- */ function openComposerWithQuote(quotedId) { var compose = document.querySelector('[data-beep-compose]'); if (!compose) return; var form = compose.querySelector('.beep-compose-body'); if (form) form.style.display = 'block'; compose.scrollIntoView({ behavior: 'smooth', block: 'center' }); fetch(config.restUrl + 'quote/' + quotedId, { headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function(quoted) { compose._beepQuotedPost = { id: quotedId, data: quoted }; showQuotePreview(compose, quoted); }) .catch(function(err) { console.warn('Could not load quoted beep:', err); }); } function showQuotePreview(compose, quoted) { var preview = compose && compose.querySelector('[data-beep-quote-preview]'); var removeBtn = compose && compose.querySelector('[data-beep-quote-remove]'); if (!preview || !quoted) return; var author = quoted.author || {}; var avatar = author.avatar || 'https://www.gravatar.com/avatar/?s=48&d=mp'; preview.innerHTML = '
' + '
' + (author.name || '') + '
' + '
' + '
' + (author.name || 'Unknown') + ' ' + '@' + (author.username || '') + ' ' + '· ' + '' + (quoted.time_ago || '') + '
' + '
' + (quoted.content || '') + '
'; preview.hidden = false; if (removeBtn) removeBtn.hidden = false; } function clearQuotePreview(compose) { if (!compose) return; compose._beepQuotedPost = null; var preview = compose.querySelector('[data-beep-quote-preview]'); var removeBtn = compose.querySelector('[data-beep-quote-remove]'); if (preview) { preview.innerHTML = ''; preview.hidden = true; } if (removeBtn) removeBtn.hidden = true; } /* ---------- GIF Picker (Giphy) ---------- */ var GIPHY_API_KEY = (window.BeepConfig && window.BeepConfig.giphyKey) || ''; function openGifPicker(compose) { var modal = document.querySelector('[data-beep-gif-modal]'); if (!modal) return; modal.hidden = false; var searchInput = modal.querySelector('[data-beep-gif-search]'); if (searchInput) searchInput.value = ''; var grid = modal.querySelector('[data-beep-gif-grid]'); if (grid) grid.innerHTML = '

Type to search GIFs...

'; if (searchInput) searchInput.focus(); } function closeGifPicker() { var modal = document.querySelector('[data-beep-gif-modal]'); if (modal) modal.hidden = true; } function searchGifs(query, compose) { if (!query || !query.trim()) return; var modal = document.querySelector('[data-beep-gif-modal]'); var grid = modal && modal.querySelector('[data-beep-gif-grid]'); if (!grid) return; grid.innerHTML = '

Searching...

'; var apiKey = (window.BeepConfig && window.BeepConfig.giphyKey) || GIPHY_API_KEY; var url = `https://api.giphy.com/v1/gifs/search?api_key=${apiKey}&q=${encodeURIComponent(query)}&limit=30&rating=g`; fetch(url) .then(function(res) { return res.json(); }) .then(function(data) { if (!data.data || !data.data.length) { grid.innerHTML = '

No GIFs found.

'; return; } grid.innerHTML = ''; data.data.forEach(function(gif) { var url = gif.images && gif.images.fixed_width_small && gif.images.fixed_width_small.url; if (!url) return; var item = document.createElement('div'); item.className = 'beep-gif-item'; item.style.cssText = 'cursor:pointer;border-radius:6px;overflow:hidden;transition:opacity 0.15s;'; item.innerHTML = '' + (gif.title || '') + ''; item.addEventListener('click', function() { selectGif(gif, compose); }); grid.appendChild(item); }); }) .catch(function() { grid.innerHTML = '

Search failed.

'; }); } function selectGif(gif, compose) { if (!compose) return; var url = gif.images && gif.images.fixed_width && gif.images.fixed_width.url; if (!url) return; compose._beepGifUrl = url; var preview = compose.querySelector('[data-beep-gif-preview]'); if (preview) { preview.innerHTML = '
GIF
'; preview.hidden = false; } closeGifPicker(); } /* ---------- Poll helpers ---------- */ function openPollSection(compose) { var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (!pollSection) return; pollSection.hidden = false; // Focus question input var questionInput = pollSection.querySelector('[data-beep-poll-question]'); if (questionInput) questionInput.focus(); // Ensure we have at least 2 options var options = pollSection.querySelectorAll('[data-beep-poll-option]'); if (options.length < 2) { while (options.length < 2) { addPollOption(compose); options = pollSection.querySelectorAll('[data-beep-poll-option]'); } } } function addPollOption(compose) { var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (!pollSection) return; var optionsContainer = pollSection.querySelector('[data-beep-poll-options]'); if (!optionsContainer) return; var optionCount = optionsContainer.querySelectorAll('[data-beep-poll-option]').length; if (optionCount >= 6) return; // Max 6 options var newRow = document.createElement('div'); newRow.className = 'beep-poll-option-row'; newRow.innerHTML = ''; optionsContainer.appendChild(newRow); updatePollButtons(compose); } function removePollOption(compose) { var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (!pollSection) return; var options = pollSection.querySelectorAll('[data-beep-poll-option]'); if (options.length <= 2) return; // Min 2 options var lastOption = options[options.length - 1]; if (lastOption) lastOption.parentElement.remove(); updatePollButtons(compose); } function updatePollButtons(compose) { var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (!pollSection) return; var options = pollSection.querySelectorAll('[data-beep-poll-option]'); var addBtn = pollSection.querySelector('[data-beep-poll-add-option]'); var removeBtn = pollSection.querySelector('[data-beep-poll-remove-option]'); if (addBtn) addBtn.hidden = options.length >= 6; if (removeBtn) removeBtn.hidden = options.length <= 2; } function clearPoll(compose) { if (!compose) return; compose._beepPoll = null; var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (pollSection) { pollSection.hidden = true; var question = pollSection.querySelector('[data-beep-poll-question]'); if (question) question.value = ''; var options = pollSection.querySelectorAll('[data-beep-poll-option]'); // Reset to 2 options if (options.length > 2) { for (var i = options.length - 1; i >= 2; i--) { if (options[i].parentElement) options[i].parentElement.remove(); } } options = pollSection.querySelectorAll('[data-beep-poll-option]'); for (var j = 0; j < options.length; j++) { options[j].value = ''; } } } function collectPollData(compose) { if (!compose) return null; var pollSection = compose.querySelector('[data-beep-compose-poll]'); if (!pollSection || pollSection.hidden) return null; var questionInput = pollSection.querySelector('[data-beep-poll-question]'); var question = questionInput ? questionInput.value.trim() : ''; if (!question) return null; var optionInputs = pollSection.querySelectorAll('[data-beep-poll-option]'); var options = []; for (var i = 0; i < optionInputs.length; i++) { var val = optionInputs[i].value.trim(); if (val) options.push(val); } if (options.length < 2) return null; return { question: question, options: options }; } function createPoll(beepId, compose) { var pollData = collectPollData(compose); if (!pollData) return Promise.resolve(); return fetch(config.restUrl + 'poll/' + beepId, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: JSON.stringify(pollData) }) .then(handleResponse) .catch(function(err) { console.warn('Poll creation failed:', err); }); } function handlePollVote(btn) { var beepId = btn.dataset.beepPollBeep; var optionIndex = parseInt(btn.dataset.beepPollVote, 10); if (!beepId || isNaN(optionIndex)) return; btn.disabled = true; fetch(config.restUrl + 'poll/' + beepId + '/vote', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce }, credentials: 'same-origin', body: JSON.stringify({ option_index: optionIndex }) }) .then(handleResponse) .then(function(data) { // Refresh the beep post to show updated poll results var postEl = btn.closest('[data-beep-post]'); if (postEl) { var list = postEl.closest('[data-beep-list]'); if (list) reloadFeed(list.closest('[data-beep-feed]'), 'latest'); } }) .catch(function() { btn.disabled = false; }); } // Magic Login Form Handler document.addEventListener('DOMContentLoaded', function() { var magicForm = document.querySelector('[data-beep-magic-form]'); if (magicForm) { magicForm.addEventListener('submit', function(e) { e.preventDefault(); var emailInput = magicForm.querySelector('[name="email"]'); var submitBtn = magicForm.querySelector('[data-beep-magic-submit]'); var btnText = submitBtn.querySelector('.beep-magic-btn-text'); var loading = submitBtn.querySelector('.beep-magic-loading'); var successMsg = document.querySelector('[data-beep-magic-success]'); var errorMsg = document.querySelector('[data-beep-magic-error]'); var email = emailInput.value; // Hide previous messages if (successMsg) successMsg.hidden = true; if (errorMsg) errorMsg.hidden = true; // Show loading btnText.hidden = true; loading.hidden = false; submitBtn.disabled = true; // Get REST URL and nonce from page var restUrl = typeof BeepConfig !== 'undefined' ? BeepConfig.restUrl : '/wp-json/'; var nonce = typeof BeepConfig !== 'undefined' ? BeepConfig.nonce : ''; fetch(restUrl + 'magic-link', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce }, body: JSON.stringify({ email: email }) }) .then(function(response) { return response.json(); }) .then(function(data) { if (data.success || (data.message && data.message.includes('sent'))) { // Success if (successMsg) { successMsg.hidden = false; magicForm.hidden = true; } } else { // Error var errorText = data.message || 'Something went wrong. Please try again.'; if (errorMsg) { errorMsg.textContent = errorText; errorMsg.hidden = false; } } }) .catch(function(err) { console.error('Magic login error:', err); if (errorMsg) { errorMsg.textContent = 'Something went wrong. Please try again.'; errorMsg.hidden = false; } }) .finally(function() { btnText.hidden = false; loading.hidden = true; submitBtn.disabled = false; }); }); } });