1// Fan messages: submit form + lazy-loaded (infinite scroll) list for #messages-modal 2(function () { 3 const config = window.loveSpellsMessages; 4 5 if (!config) { 6 return; 7 } 8 9 const container = document.getElementById('messages-container'); 10 const sentinel = document.getElementById('messages-sentinel'); 11 const form = document.getElementById('message-form'); 12 const input = document.getElementById('message-input'); 13 const errorEl = document.getElementById('message-error'); 14 15 let loading = false; 16 let nextPage = 2; 17 let hasMore = sentinel ? sentinel.dataset.hasMore === '1' : false; 18 19 function loadMoreMessages() { 20 if (loading || !hasMore || !sentinel) { 21 return; 22 } 23 24 loading = true; 25 sentinel.classList.add('is-loading'); 26 27 const body = new URLSearchParams({ 28 action: 'love_spells_get_messages', 29 nonce: config.nonce, 30 page: nextPage, 31 }); 32 33 fetch(config.ajaxUrl, { 34 method: 'POST', 35 credentials: 'same-origin', 36 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 37 body, 38 }) 39 .then((response) => response.json()) 40 .then((data) => { 41 if (data.success) { 42 sentinel.insertAdjacentHTML('beforebegin', data.data.html); 43 hasMore = Boolean(data.data.hasMore); 44 nextPage += 1; 45 } else { 46 hasMore = false; 47 } 48 }) 49 .catch(() => { 50 hasMore = false; 51 }) 52 .finally(() => { 53 loading = false; 54 sentinel.classList.remove('is-loading'); 55 }); 56 } 57 58 if (sentinel && container) { 59 const observer = new IntersectionObserver( 60 (entries) => { 61 entries.forEach((entry) => { 62 if (entry.isIntersecting) { 63 loadMoreMessages(); 64 } 65 }); 66 }, 67 { root: container, threshold: 0.1 } 68 ); 69 70 observer.observe(sentinel); 71 } 72 73 if (form && input && container) { 74 input.addEventListener('input', function () { 75 if (errorEl) { 76 errorEl.textContent = ''; 77 } 78 }); 79 80 form.addEventListener('submit', function (event) { 81 event.preventDefault(); 82 83 const message = input.value.trim(); 84 85 if (errorEl) { 86 errorEl.textContent = ''; 87 } 88 89 if (!message) { 90 return; 91 } 92 93 const submitButton = form.querySelector('button[type="submit"]'); 94 if (submitButton) { 95 submitButton.disabled = true; 96 } 97 98 const body = new URLSearchParams({ 99 action: 'love_spells_submit_message', 100 nonce: config.nonce, 101 message, 102 }); 103 104 fetch(config.ajaxUrl, { 105 method: 'POST', 106 credentials: 'same-origin', 107 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 108 body, 109 }) 110 .then((response) => response.json()) 111 .then((data) => { 112 if (data.success) { 113 container.insertAdjacentHTML('afterbegin', data.data.html); 114 form.reset(); 115 } else if (errorEl) { 116 errorEl.textContent = 117 (data.data && data.data.message) || 118 'Could not save your message. Please try again.'; 119 } 120 }) 121 .catch(() => { 122 if (errorEl) { 123 errorEl.textContent = 'Could not save your message. Please try again.'; 124 } 125 }) 126 .finally(() => { 127 if (submitButton) { 128 submitButton.disabled = false; 129 } 130 }); 131 }); 132 } 133})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.