1(function ($) { 2 'use strict'; 3 4 // Stato globale minimale (un solo polling per pagina) 5 var GLOBAL = window.PRCHAT_APP || (window.PRCHAT_APP = { 6 pollId: null, 7 initedOnce: false 8 }); 9 10 // Chiavi localStorage condivise (no cookie) 11 var LS = { 12 USERNAME: 'prchat_username', 13 PRIVACY: 'prchat_privacy', 14 YOB: 'prchat_yob' 15 }; 16 17 // --------- UTIL ---------- 18 function lsGet(key, def) { try { var v = localStorage.getItem(key); return (v==null)?def:v; } catch(e){ return def; } } 19 function lsSet(key, val) { try { localStorage.setItem(key, val); } catch(e){} } 20 function lsDel(key) { try { localStorage.removeItem(key); } catch(e){} } 21 22 function nowYear(){ return (new Date()).getFullYear(); } 23 24 function renderMessage(m){ 25 var time = new Date(m.t*1000).toLocaleTimeString(); 26 return '<div class="prchat-rowmsg" data-id="'+m.id+'">' + 27 '<span class="prchat-time">['+time+']</span> ' + 28 '<span class="prchat-user">'+m.u+':</span> ' + 29 '<span class="prchat-text">'+m.m+'</span>' + 30 '</div>'; 31 } 32 33 // --------- ISTANZA ---------- 34 // Ogni wrapper gestisce il proprio stato, salvato via $.data 35 function getState($wrap){ 36 var st = $wrap.data('prchatState'); 37 if (!st) { 38 st = { 39 lastId: 0, 40 busy: false, 41 unlocked: !PRCHAT.agegate, // se agegate disattivo, sbloccato by default 42 yob: null, 43 initialized: false 44 }; 45 $wrap.data('prchatState', st); 46 } 47 return st; 48 } 49 50 function scope($wrap, sel){ 51 // limita la ricerca all'interno del wrapper (supporta id duplicati) 52 return $wrap.find(sel); 53 } 54 55 function buildEmojiPicker($wrap){ 56 var $pop = scope($wrap, '#prchat-emoji-pop'); 57 if (!$pop.length) return; 58 var list = (window.PRCHAT && PRCHAT.emojis) || []; 59 var html = '<div class="prchat-emoji-grid">'; 60 for (var i=0;i<list.length;i++){ 61 var e = list[i]; 62 html += '<button type="button" class="prchat-emoji-item" data-emoji="'+e+'">'+e+'</button>'; 63 } 64 html += '</div>'; 65 $pop.html(html); 66 } 67 68 function buildYOB($wrap){ 69 if (!PRCHAT.agegate) return; 70 var $sel = scope($wrap, '#prchat-yob'); 71 if (!$sel.length) return; 72 if ($sel.children('option').length === 0) { 73 $sel.append('<option value="">'+PRCHAT.strings.yob+'</option>'); 74 var yMax = PRCHAT.yMax || nowYear(); 75 var yMin = PRCHAT.yMin || 1900; 76 for (var y=yMax; y>=yMin; y--) $sel.append('<option value="'+y+'">'+y+'</option>'); 77 } 78 } 79 80 function prefillFromLocal($wrap){ 81 var st = getState($wrap); 82 var u = lsGet(LS.USERNAME, ''); 83 if (u) scope($wrap, '#prchat-username').val(u); 84 85 var pv = lsGet(LS.PRIVACY, ''); 86 if (pv === '1') scope($wrap, '#prchat-privacy').prop('checked', true); 87 88 var yob = lsGet(LS.YOB, ''); 89 if (yob) { 90 scope($wrap, '#prchat-yob').val(yob); 91 st.yob = yob; 92 if (PRCHAT.agegate) { 93 var age = nowYear() - parseInt(yob, 10); 94 if (PRCHAT.minAge && age >= PRCHAT.minAge) { 95 st.unlocked = true; 96 $wrap.removeClass('prchat-locked'); 97 scope($wrap, '#prchat-agegate').remove(); 98 } 99 } 100 } 101 } 102 103 function insertAtCursor($input, text) { 104 var el = $input.get(0); 105 if (!el) return; 106 el.focus(); 107 if (typeof el.selectionStart === 'number') { 108 var start = el.selectionStart, end = el.selectionEnd; 109 var val = el.value; 110 el.value = val.slice(0,start) + text + val.slice(end); 111 el.selectionStart = el.selectionEnd = start + text.length; 112 } else { 113 el.value += text; 114 } 115 $input.trigger('input'); 116 } 117 118 function lockUsername($wrap){ 119 var $u = scope($wrap, '#prchat-username'); 120 if ($u.length && !$u.prop('disabled')) { 121 $u.prop('disabled', true).attr('title', 'Username locked for this session'); 122 } 123 } 124 125 // GET messaggi per istanza 126 function loadMessages($wrap){ 127 var st = getState($wrap); 128 if (PRCHAT.agegate && !st.unlocked) return; 129 if (st.busy) return; 130 131 var $body = scope($wrap, '#prchat-body'); 132 if (!$body.length) return; 133 134 st.busy = true; 135 $.post(PRCHAT.ajax, { 136 action: 'prchat_get', 137 nonce: PRCHAT.nonce, 138 since: st.lastId 139 }, function(resp){ 140 if (resp && resp.success && resp.data && resp.data.messages) {
141 resp.data.messages.forEach(function(m){ 142 $body.append(renderMessage(m)); 143 st.lastId = Math.max(st.lastId, m.id); 144 }); 145 if (resp.data.messages && resp.data.messages.length) { 146 $body.scrollTop($body[0].scrollHeight); 147 } 148 } 149 }).always(function(){ st.busy = false; }); 150 } 151 152 // POST messaggio per istanza 153 function sendMessage($wrap, u, m, pv, hp) { 154 var st = getState($wrap); 155 return $.post(PRCHAT.ajax, { 156 action: 'prchat_post', 157 nonce: PRCHAT.nonce, 158 u: u, 159 m: m, 160 pv: pv ? '1' : '0', 161 hp: hp || '', 162 yob: st.yob || '' 163 }); 164 } 165 166 // Inizializza UNA istanza 167 function initInstance($wrap){ 168 var st = getState($wrap); 169 if (st.initialized) return; 170 171 // Build UI parziali 172 buildEmojiPicker($wrap); 173 buildYOB($wrap); 174 prefillFromLocal($wrap); 175 176 // Primo load se già sbloccata 177 if (st.unlocked) loadMessages($wrap); 178 179 st.initialized = true; 180 $wrap.attr('data-prchat-initialized', 'true'); 181 } 182 183 // Inizializza TUTTE le istanze presenti 184 function initAll(){ 185 $('.prchat-wrap').each(function(){ 186 initInstance($(this)); 187 }); 188 } 189 190 // --------- EVENTI DELEGATI (una sola volta a documento) ---------- 191 function bindDelegatedEvents(){ 192 if (GLOBAL.initedOnce) return; 193 GLOBAL.initedOnce = true; 194 195 // Unlock (per istanza) 196 $(document).on('click', '#prchat-unlock', function(){ 197 var $wrap = $(this).closest('.prchat-wrap'); 198 var st = getState($wrap); 199 var $yob = scope($wrap, '#prchat-yob'); 200 var yob = $yob.val(); 201 if (!yob) { alert(PRCHAT.strings.yob); return; } 202 st.yob = yob; 203 lsSet(LS.YOB, yob); 204 var age = nowYear() - parseInt(yob, 10); 205 if (PRCHAT.minAge && age < PRCHAT.minAge) { alert(PRCHAT.strings.age); return; } 206 st.unlocked = true; 207 $wrap.removeClass('prchat-locked'); 208 scope($wrap, '#prchat-agegate').remove(); 209 loadMessages($wrap); 210 }); 211 212 // Emoji toggle (istanza) 213 $(document).on('click', '#prchat-emoji-btn', function(e){ 214 e.preventDefault(); 215 var $wrap = $(this).closest('.prchat-wrap'); 216 var $pop = scope($wrap, '#prchat-emoji-pop'); 217 var isOpen = !$pop.attr('hidden'); 218 if (isOpen) { $pop.attr('hidden', true); $(this).attr('aria-expanded','false'); } 219 else { $pop.attr('hidden', false); $(this).attr('aria-expanded','true'); } 220 }); 221 222 // Emoji chiusura click fuori 223 $(document).on('click', function(e){ 224 var $t = $(e.target); 225 if (!$t.closest('#prchat-emoji-pop').length && !$t.closest('#prchat-emoji-btn').length) { 226 $('.prchat-emoji-pop').attr('hidden', true); 227 $('#prchat-emoji-btn').attr('aria-expanded','false'); 228 } 229 }); 230 231 // Emoji pick (istanza) 232 $(document).on('click', '.prchat-emoji-item', function(){ 233 var $wrap = $(this).closest('.prchat-wrap'); 234 var $input = scope($wrap, '#prchat-message'); 235 insertAtCursor($input, $(this).data('emoji')); 236 }); 237 238 // Persistenza campi 239 $(document).on('input', '#prchat-username', function(){ 240 lsSet(LS.USERNAME, $.trim($(this).val())); 241 }); 242 $(document).on('change', '#prchat-privacy', function(){ 243 lsSet(LS.PRIVACY, $(this).is(':checked') ? '1' : '0'); 244 }); 245 $(document).on('change', '#prchat-yob', function(){ 246 lsSet(LS.YOB, $(this).val() || ''); 247 }); 248 249 // Submit (istanza) 250 $(document).on('submit', '#prchat-form', function(e){ 251 e.preventDefault(); 252 var $wrap = $(this).closest('.prchat-wrap'); 253 var st = getState($wrap); 254 255 if (PRCHAT.agegate && !st.unlocked) { alert(PRCHAT.strings.unlock); return; } 256 257 var $u = scope($wrap, '#prchat-username'); 258 var $m = scope($wrap, '#prchat-message'); 259 var $pv = scope($wrap, '#prchat-privacy'); 260 var $hp = scope($wrap, '#prchat_hp'); 261 262 var u = $.trim($u.val()); 263 var m = $.trim($m.val()); 264 var pv = $pv.is(':checked'); 265 var hp = $hp.val(); 266 267 if (!u || !m || !pv) { alert(PRCHAT.strings.required); return; } 268 269 lsSet(LS.USERNAME, u); 270 lsSet(LS.PRIVACY, pv ? '1' : '0'); 271 272 var $btn = $(this).find('.prchat-btn').prop('disabled', true); 273 sendMessage($wrap, u, m, pv, hp).done(function(resp){ 274 if (resp && resp.success && resp.data && resp.data.message) { 275 var $body = scope($wrap, '#prchat-body'); 276 var msg = resp.data.message; 277 $body.append(renderMessage(msg)); 278 st.lastId = Math.max(st.lastId, msg.id); 279 $m.val(''); 280 $body.scrollTop($body[0].scrollHeight); 281 // lock username per istanza 282 lockUsername($wrap); 283 } else if (resp && resp.data && resp.data.error === 'rate') { 284 alert(PRCHAT.strings.rate); 285 } else if (resp && resp.data && resp.data.error === 'age') { 286 alert(PRCHAT.strings.age); 287 } 288 }).fail(function(xhr){ 289 if (xhr && xhr.responseJSON) { 290 var err = xhr.responseJSON.data && xhr.responseJSON.data.error; 291 if (err === 'banned') alert(PRCHAT.strings.banned); 292 else if (err === 'rate') alert(PRCHAT.strings.rate); 293 else if (err === 'age') alert(PRCHAT.strings.age); 294 } 295 }).always(function(){ $btn.prop('disabled', false); }); 296 }); 297 } 298 299 // --------- POLLING GLOBALE ---------- 300 function ensurePolling(){ 301 if (GLOBAL.pollId) return; 302 GLOBAL.pollId = setInterval(function(){ 303 $('.prchat-wrap').each(function(){ 304 var $wrap = $(this); 305 var st = getState($wrap); 306 if (!st.initialized) return; // non ancora pronto 307 loadMessages($wrap); 308 }); 309 }, 4000); 310 } 311 312 // --------- BOOT ---------- 313 function boot(){ 314 bindDelegatedEvents(); 315 initAll(); 316 ensurePolling(); 317 } 318 319 // Primo avvio 320 $(function(){ boot(); }); 321 322 // AJAX nav custom (ProRadio) 323 $(window).on('qtAjaxpageLoadEnd', function(){
324 boot(); 325 }); 326 327 // Elementor / PJAX / altri injector 328 try { 329 var obs = new MutationObserver(function(){ 330 boot(); 331 }); 332 obs.observe(document.body, { childList: true, subtree: true }); 333 } catch(e){ /* noop */ } 334 335})(jQuery); 336 337 338 339 340 341 342 343
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.