1// --- ENGINE MÃLTIPLA DE COMENTÃRIOS INSTANCIADOS (STYLE FACEBOOK) --- 2document.addEventListener('DOMContentLoaded', () => { 3 4 // Função Criadora de Escopo Fechado (Closure Constructor) 5 // Tudo que rola aqui dentro afeta APENAS O FORMULÃRIO que foi passado (container) 6 window.initCommentForm = function(container) { 7 if(!container) return; 8 9 // Core Elements Isolados 10 const textarea = container.querySelector('.sp-comment-textarea'); 11 const submitBtn = container.querySelector('.sp-comment-submit-btn'); 12 const formPanel = container.querySelector('.sp-advanced-comment-form'); 13 const postIdInput = container.querySelector('.sp_post_id'); 14 const postId = postIdInput ? postIdInput.value : ''; 15 const parentIdInput = container.querySelector('.sp_comment_parent'); 16 const editIdInput = container.querySelector('.sp_edit_comment_id'); 17 const spinner = container.querySelector('.sp-comment-spinner'); 18 19 // Emojis (Native OS Trigger approach) 20 const emojiBtn = container.querySelector('.sp-emoji-trigger'); 21 22 // Mentions Elements 23 const mentionsDropdown = container.querySelector('.sp-mentions-dropdown'); 24 const mentionsList = container.querySelector('.sp-mentions-list'); 25 26 // Estado da Menção Local 27 let isMentioning = false; 28 let mentionQuery = ''; 29 let currentFocus = 0; 30 31 if(!textarea) return; 32 33 // 1. Controle UI do ContentEditable 34 textarea.addEventListener('input', function() { 35 const hasMedia = this.querySelector('img, iframe, .sp-yt-embed-preview'); 36 if(this.textContent.trim().length > 0 || hasMedia) { 37 submitBtn.classList.remove('opacity-50', 'cursor-not-allowed', 'bg-gray-100', 'dark:bg-darksurface', 'text-gray-500'); 38 submitBtn.classList.add('bg-seleblue', 'text-white', 'shadow-md', 'pointer-events-auto'); 39 } else { 40 submitBtn.classList.add('opacity-50', 'cursor-not-allowed', 'bg-gray-100', 'dark:bg-darksurface', 'text-gray-500'); 41 submitBtn.classList.remove('bg-seleblue', 'text-white', 'shadow-md', 'pointer-events-auto'); 42 } 43 }); 44 45 const mentionBtn = container.querySelector('.sp-mention-trigger'); 46 if(mentionBtn) { 47 mentionBtn.addEventListener('click', () => { 48 textarea.focus(); 49 if(typeof closeEmojiDropdown === 'function') closeEmojiDropdown(); 50 if(typeof closeVideoDropdown === 'function') closeVideoDropdown(); 51 document.execCommand('insertText', false, '@'); 52 textarea.dispatchEvent(new KeyboardEvent('keyup', { key: '@', bubbles: true })); 53 }); 54 } 55 56 const imageUploadInput = container.querySelector('.sp-comment-image-upload'); 57 const imageTrigger = container.querySelector('.sp-image-trigger'); 58 if (imageTrigger && imageUploadInput) { 59 imageTrigger.addEventListener('click', () => { 60 // Aqui podemos injetar var do php no header. 61 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 62 const loginBtn = document.querySelector('.trigger-login-modal'); 63 if(loginBtn) loginBtn.click(); 64 return; 65 } 66 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-preview').length >= 1) { 67 alert('Você já anexou uma mÃdia neste comentário! Remova a atual para inserir outra.'); 68 return; 69 } 70 imageUploadInput.click(); 71 }); 72 } 73 74 if (imageUploadInput) { 75 imageUploadInput.addEventListener('change', function(e) { 76 const file = e.target.files[0]; 77 if (!file) return; 78 79 if (file.size > 2 * 1024 * 1024) { 80 alert('A imagem é muito grande. O limite máximo é 2MB.'); 81 this.value = ''; 82 return; 83 } 84 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
84preview').length >= 1) { 85 alert('Você já anexou uma mÃdia neste comentário! Remova a atual para inserir outra.'); 86 this.value = ''; 87 return; 88 } 89 if (!file.type.match(/^image\/(jpeg|png|webp|gif)$/)) { 90 alert('Formato não suportado. Use JPEG, PNG, WEBP ou GIF.'); 91 this.value = ''; 92 return; 93 } 94 95 textarea.setAttribute('contenteditable', 'false'); 96 textarea.style.opacity = '0.5'; 97 submitBtn.disabled = true; 98 99 const reader = new FileReader(); 100 reader.onload = function(event) { 101 const img = new Image(); 102 img.onload = function() { 103 const canvas = document.createElement('canvas'); 104 const MAX_DIM = 1080; 105 let width = img.width; 106 let height = img.height; 107 108 if (width > MAX_DIM || height > MAX_DIM) { 109 if (width > height) { 110 height = Math.round(height * (MAX_DIM / width)); 111 width = MAX_DIM; 112 } else { 113 width = Math.round(width * (MAX_DIM / height)); 114 height = MAX_DIM; 115 } 116 } 117 118 canvas.width = width; 119 canvas.height = height; 120 const ctx = canvas.getContext('2d'); 121 ctx.drawImage(img, 0, 0, width, height); 122 123 const base64Image = canvas.toDataURL('image/jpeg', 0.70); 124 125 const formData = new FormData(); 126 formData.append('action', 'sp_upload_comment_media'); 127 formData.append('image_data', base64Image); 128 formData.append('nonce', selepowerAjax.media_nonce); 129 130 fetch(selepowerAjax.ajaxurl, { method: 'POST', body: formData }) 131 .then(res => res.json()) 132 .then(res => { 133 textarea.setAttribute('contenteditable', 'true'); 134 textarea.style.opacity = '1'; 135 submitBtn.disabled = false; 136 137 if (res.success) { 138 textarea.focus(); 139 const htmlBlob = `<br><img src="${res.data.url}" class="sp-comment-upload max-w-full rounded-lg shadow-sm border border-gray-100 dark:border-gray-800 my-2" contenteditable="false"><br>`; 140 document.execCommand('insertHTML', false, htmlBlob); 141 textarea.dispatchEvent(new Event('input')); 142 } else { 143 alert(res.data.message || 'Falha ao processar arquivo no servidor.'); 144 } 145 }) 146 .catch(e => { 147 textarea.setAttribute('contenteditable', 'true'); 148 textarea.style.opacity = '1'; 149 submitBtn.disabled = false; 150 alert('Erro de conexão ao enviar imagem.'); 151 }); 152 }; 153 img.src = event.target.result; 154 }; 155 reader.readAsDataURL(file); 156 this.value = ''; 157 }); 158 } 159 160 textarea.addEventListener('keyup', (e) => { 161 if (isMentioning && (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter')) return; 162 const sel = window.getSelection(); 163 if(!sel.rangeCount) return; 164 const range = sel.getRangeAt(0); 165 166 if (range.startContainer.parentNode && range.startContainer.parentNode.tagName === 'SPAN') { 167 closeMentionsDropdown(); return; 168 } 169 if (range.startContainer.nodeType !== Node.TEXT_NODE) return; 170 171 const cursorOffset = range.startOffset; 172 const textToCursor = range.startContainer.textContent.substring(0, cursorOffset); 173 const match = textToCursor.match(/(?:^|\s)@([\w.-]*)$/); 174 175 if (match !== null) { 176 isMentioning = true; 177 mentionQuery = match[1]; 178 container.spCurrentRange = range.cloneRange(); 179 container.spMentionQueryLength = match[1].length + 1; 180 181 if (mentionQuery.length >= 2) { 182 fetch(selepowerAjax.ajaxurl + '?action=sp_search_mention_users&q=' + encodeURIComponent(mentionQuery)) 183 .then(res => res.json()) 184 .then(data => { 185 if(data.success && data.data.length > 0) renderMentionsDropdown(data.data); 186 else closeMentionsDropdown(); 187 }); 188 } else { 189 mentionsDropdown.classList.remove('scale-95', 'opacity-0', 'hidden'); 190 mentionsList.innerHTML = '<li class="p-3 text-xs text-gray-500 text-center">Continue digitando...</li>'; 191 } 192 } else { 193 closeMentionsDropdown(); 194 } 195 }); 196 197 textarea.addEventListener('keydown', (e) => { 198 if (!isMentioning) return; 199 let items = mentionsList.querySelectorAll('li.mention-item'); 200 if (items.length === 0) return; 201 if (e.key === 'ArrowDown') { currentFocus++; addActive(items); e.preventDefault(); } 202 else if (e.key === 'ArrowUp') { currentFocus--; addActive(items); e.preventDefault(); } 203 else if (e.key === 'Enter') { 204 e.preventDefault(); 205 if (currentFocus > -1 && items[currentFocus]) items[currentFocus].click(); 206 } 207 }); 208 209 function addActive(items) { 210 for (let i = 0; i < items.length; i++) { 211 items[i].classList.remove('bg-blue-50', 'dark:bg-blue-900/30', 'border-seleblue'); 212 items[i].classList.add('border-transparent'); 213 } 214 if (currentFocus >= items.length) currentFocus = 0; 215 if (currentFocus < 0) currentFocus = (items.length - 1); 216 items[currentFocus].classList.add('bg-blue-50', 'dark:bg-blue-900/30', 'border-seleblue'); 217 items[currentFocus].classList.remove('border-transparent'); 218 } 219 220 function renderMentionsDropdown(users) { 221 mentionsList.innerHTML = ''; 222 currentFocus = 0;
223 users.forEach((user, index) => { 224 const li = document.createElement('li'); 225 li.className = `mention-item cursor-pointer p-2 flex items-center gap-2 border-l-2 transition-colors ${index === 0 ? 'bg-blue-50 dark:bg-blue-900/30 border-seleblue' : 'border-transparent hover:bg-gray-50 dark:hover:bg-[#111]'}`; 226 li.innerHTML = `<img src="${user.avatar}" class="w-6 h-6 rounded-full border border-gray-200 dark:border-gray-700"><div class="flex flex-col leading-tight"><span class="text-sm font-semibold text-gray-800 dark:text-gray-200">${user.name}</span><span class="text-[10px] text-gray-500">@${user.slug}</span></div>`; 227 228 li.addEventListener('click', function(e) { 229 e.preventDefault(); 230 const sel = window.getSelection(); 231 if (container.spCurrentRange) { 232 sel.removeAllRanges(); 233 sel.addRange(container.spCurrentRange); 234 for(let i=0; i < container.spMentionQueryLength; i++){ 235 document.execCommand('delete', false, null); 236 } 237 const htmlBlob = ` <span class="text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded" contenteditable="false" data-userid="${user.id}">${user.name}</span> `; 238 document.execCommand('insertHTML', false, htmlBlob); 239 } 240 textarea.dispatchEvent(new Event('input')); 241 closeMentionsDropdown(); 242 }); 243 mentionsList.appendChild(li); 244 }); 245 mentionsDropdown.classList.remove('hidden'); 246 setTimeout(() => mentionsDropdown.classList.remove('scale-95', 'opacity-0'), 10); 247 } 248 249 const closeMentionsDropdown = () => { 250 isMentioning = false; 251 mentionsDropdown.classList.add('scale-95', 'opacity-0'); 252 setTimeout(() => mentionsDropdown.classList.add('hidden'), 150); 253 currentFocus = -1; 254 }; 255 256 // Emojis Local 257 const emojiDropdown = container.querySelector('.sp-emoji-dropdown'); 258 const emojiGrid = container.querySelector('.sp-emoji-grid'); 259 const spEmojis = ["ð","ð","ð","ð¥","â¤ï¸","ð","ð","ð","ð","ð ","ð","ð¯","â¨","ð","ð","ð","ð¥°","ð","ð","ð","ðª","ð","ð¤¦","ð¤·","ð¬","ð±","ð¤¯","ð¤¢","ð¤®","ð¡","ð¤¬","ð","ð§ ","ð¤","ð¤","ð","âï¸","ð¤","ð","ð»","ð","ð¸","ð¤","ð","ð±","ð»","ð®","ð§"]; 260 261 if (emojiGrid && emojiGrid.innerHTML.trim() === '') { 262 spEmojis.forEach(emoji => { 263 const btn = document.createElement('button'); 264 btn.type = 'button'; 265 btn.className = 'w-8 h-8 flex items-center justify-center text-xl hover:bg-gray-100 dark:hover:bg-gray-800 rounded cursor-pointer transition-colors'; 266 btn.innerText = emoji; 267 btn.addEventListener('click', (e) => { 268 e.preventDefault(); 269 textarea.focus(); 270 if (container.spLastCaretRange) { 271 const sel = window.getSelection(); 272 sel.removeAllRanges(); 273 sel.addRange(container.spLastCaretRange); 274 } 275 document.execCommand('insertText', false, emoji); 276 textarea.dispatchEvent(new Event('input')); 277 const newSel = window.getSelection(); 278 if(newSel.rangeCount) container.spLastCaretRange = newSel.getRangeAt(0).cloneRange(); 279 }); 280 emojiGrid.appendChild(btn); 281 }); 282 } 283 284 if(emojiBtn) { 285 emojiBtn.addEventListener('click', (e) => { 286 e.preventDefault(); 287 const sel = window.getSelection(); 288 if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) { 289 container.spLastCaretRange = sel.getRangeAt(0).cloneRange(); 290 } 291 292 if (emojiDropdown.classList.contains('hidden')) { 293 closeMentionsDropdown(); 294 closeVideoDropdown(); 295 closeGiphyDropdown(); 296 emojiDropdown.classList.remove('hidden'); 297 setTimeout(() => emojiDropdown.classList.remove('scale-95', 'opacity-0'), 10); 298 } else { 299 closeEmojiDropdown(); 300 } 301 }); 302 } 303 304 const closeEmojiDropdown = () => { 305 if(emojiDropdown) { 306 emojiDropdown.classList.add('scale-95', 'opacity-0'); 307 setTimeout(() => emojiDropdown.classList.add('hidden'), 150); 308 } 309 }; 310 const cxEmoji = container.querySelector('.sp-cx-emoji'); 311 if(cxEmoji) cxEmoji.addEventListener('click', closeEmojiDropdown); 312 313 // Videos Local 314 const videoDropdown = container.querySelector('.sp-video-dropdown'); 315 const cxVideo = container.querySelector('.sp-cx-video'); 316 const ytAddBtn = container.querySelector('.sp-yt-add-btn'); 317 const ytInput = container.querySelector('.sp-yt-input'); 318 319 container.querySelector('.sp-video-trigger')?.addEventListener('click', (e) => { 320 e.preventDefault(); 321 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 322 const loginBtn = document.querySelector('.trigger-login-modal'); 323 if(loginBtn) loginBtn.click(); 324 return; 325 } 326 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
326preview').length >= 1) { 327 alert('Você já anexou uma mÃdia neste comentário! Remova a atual para inserir outra.'); 328 return; 329 } 330 331 const sel = window.getSelection(); 332 if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) { 333 container.spLastCaretRange = sel.getRangeAt(0).cloneRange(); 334 } 335 336 if (videoDropdown.classList.contains('hidden')) { 337 closeMentionsDropdown(); 338 closeEmojiDropdown(); 339 closeGiphyDropdown(); 340 videoDropdown.classList.remove('hidden'); 341 setTimeout(() => videoDropdown.classList.remove('scale-95', 'opacity-0'), 10); 342 setTimeout(() => ytInput.focus(), 150); 343 } else { 344 closeVideoDropdown(); 345 } 346 }); 347 348 const closeVideoDropdown = () => { 349 if(videoDropdown) { 350 videoDropdown.classList.add('scale-95', 'opacity-0'); 351 setTimeout(() => videoDropdown.classList.add('hidden'), 150); 352 } 353 }; 354 if(cxVideo) cxVideo.addEventListener('click', closeVideoDropdown); 355 356 if (ytAddBtn) { 357 ytAddBtn.addEventListener('click', () => { 358 const url = ytInput.value.trim(); 359 if (!url) return; 360 const match = url.match(/(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w-]{11})/); 361 if (!match || !match[1]) { 362 alert("Link inválido. Copie uma URL oficial do YouTube!"); 363 return; 364 } 365 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-preview').length >= 1) { 366 alert('Você já anexou uma mÃdia neste comentário! Remova a atual para inserir outra.'); 367 return; 368 } 369 const videoId = match[1]; 370 textarea.focus(); 371 if (container.spLastCaretRange) { 372 const sel = window.getSelection(); 373 sel.removeAllRanges(); 374 sel.addRange(container.spLastCaretRange); 375 } 376 const embedHtml = `<br><div class="sp-yt-embed-preview relative w-full max-w-[450px] aspect-video rounded-xl overflow-hidden my-2 border border-gray-200 dark:border-gray-800 pointer-events-none" contenteditable="false" data-ytid="${videoId}"> 377 <iframe width="100%" height="100%" src="https://www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen class="pointer-events-none"></iframe> 378 <div class="absolute inset-0 bg-black/10 z-10 flex items-center justify-center backdrop-blur-sm">YT</div> 379 </div><br>`; 380 document.execCommand('insertHTML', false, embedHtml); 381 ytInput.value = ''; 382 closeVideoDropdown(); 383 textarea.dispatchEvent(new Event('input')); 384 }); 385 } 386 387 // Giphy Local 388 const giphyAPIKey = 'cIMBNhFtlA70SoPkx2QfkyAdR5UbaNC3'; 389 const giphyDropdown = container.querySelector('.sp-giphy-dropdown'); 390 const giphySearch = container.querySelector('.sp-giphy-search'); 391 const giphyGrid = container.querySelector('.sp-giphy-grid'); 392 const cxGiphy = container.querySelector('.sp-cx-giphy'); 393 394 let giphyLoaded = false; let giphyDebounceTimer; let giphyOffset = 0; 395 let isFetchingGiphy = false; let currentGiphyQuery = ''; let currentGiphyType = 'trending'; let giphyHasMore = true; 396 397 container.querySelector('.sp-giphy-trigger')?.addEventListener('click', (e) => { 398 e.preventDefault(); 399 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 400 const loginBtn = document.querySelector('.trigger-login-modal'); 401 if(loginBtn) loginBtn.click(); 402 return; 403 } 404 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
404preview').length >= 1) { 405 alert('Você já anexou uma mÃdia neste comentário! Remova a atual para inserir outra.'); 406 return; 407 } 408 409 const sel = window.getSelection(); 410 if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) { 411 container.spLastCaretRange = sel.getRangeAt(0).cloneRange(); 412 } 413 414 if (giphyDropdown.classList.contains('hidden')) { 415 closeMentionsDropdown(); 416 closeEmojiDropdown(); 417 closeVideoDropdown(); 418 giphyDropdown.classList.remove('hidden'); 419 setTimeout(() => giphyDropdown.classList.remove('scale-95', 'opacity-0'), 10); 420 setTimeout(() => giphySearch.focus(), 150); 421 if(!giphyLoaded) { fetchGiphy('trending'); giphyLoaded = true; } 422 } else { 423 closeGiphyDropdown(); 424 } 425 }); 426 427 const closeGiphyDropdown = () => { 428 if(giphyDropdown) { 429 giphyDropdown.classList.add('scale-95', 'opacity-0'); 430 setTimeout(() => giphyDropdown.classList.add('hidden'), 150); 431 } 432 }; 433 if(cxGiphy) cxGiphy.addEventListener('click', closeGiphyDropdown); 434 435 if (giphySearch) { 436 giphySearch.addEventListener('input', (e) => { 437 clearTimeout(giphyDebounceTimer); 438 const query = e.target.value.trim(); 439 giphyDebounceTimer = setTimeout(() => { 440 if (query.length > 0) fetchGiphy('search', query); 441 else fetchGiphy('trending'); 442 }, 500); 443 }); 444 } 445 446 if (giphyGrid) { 447 giphyGrid.addEventListener('scroll', () => { 448 if (giphyGrid.scrollTop + giphyGrid.clientHeight >= giphyGrid.scrollHeight - 50) { 449 if(!isFetchingGiphy && giphyHasMore) fetchGiphy(currentGiphyType, currentGiphyQuery, true); 450 } 451 }); 452 } 453 454 function fetchGiphy(type, query = '', isAppend = false) { 455 if (isFetchingGiphy || (!giphyHasMore && isAppend)) return; 456 isFetchingGiphy = true; 457 currentGiphyType = type; currentGiphyQuery = query; 458 459 const loadingHtml = `<div class="col-span-2 text-center text-xs font-bold text-gray-400 py-8" id="sp-giphy-loader"><div class="inline-block w-5 h-5 border-2 border-seleblue border-t-transparent rounded-full animate-spin mb-2"></div><br>Buscando...</div>`; 460 if (!isAppend) { 461 giphyOffset = 0; giphyHasMore = true; giphyGrid.innerHTML = loadingHtml; 462 } else { 463 giphyGrid.insertAdjacentHTML('beforeend', loadingHtml); 464 } 465 466 let endpoint = `https://api.giphy.com/v1/gifs/trending?api_key=${giphyAPIKey}&limit=12&rating=g&offset=${giphyOffset}`; 467 if (type === 'search') { 468 const qEncoded = encodeURIComponent(query); 469 endpoint = `https://api.giphy.com/v1/gifs/search?api_key=${giphyAPIKey}&q=${qEncoded}&limit=12&rating=g&lang=pt&offset=${giphyOffset}`; 470 } 471 472 fetch(endpoint).then(res => res.json()).then(data => { 473 isFetchingGiphy = false; 474 const activeLoader = container.querySelector('#sp-giphy-loader'); 475 if (activeLoader) activeLoader.remove(); 476 if (!isAppend) giphyGrid.innerHTML = ''; 477 478 if(data.data && data.data.length > 0) { 479 giphyOffset += data.data.length; 480 if(data.pagination && data.pagination.total_count <= giphyOffset) giphyHasMore = false; 481 data.data.forEach(gif => { 482 const imgUrl = gif.images.fixed_width_downsampled.url; 483 const finalImgUrl = gif.images.fixed_height.url; 484 485 const btn = document.createElement('button'); 486 btn.type = 'button'; 487 btn.className = 'w-full h-[100px] overflow-hidden rounded border border-gray-200 dark:border-gray-800 focus:border-seleblue focus:outline-none transition-all hover:opacity-80 active:scale-95'; 488 btn.innerHTML = `<img src="${imgUrl}" class="w-full h-full object-cover" loading="lazy">`; 489 490 btn.addEventListener('click', (e) => { 491 e.preventDefault(); 492 if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
492preview').length >= 1) { 493 alert('Você só pode anexar 1 mÃdia (Imagem, GIF ou YouTube) por comentário!'); 494 closeGiphyDropdown(); return; 495 } 496 textarea.focus(); 497 if (container.spLastCaretRange) { 498 const sel = window.getSelection(); 499 sel.removeAllRanges(); sel.addRange(container.spLastCaretRange); 500 } 501 const embedHtml = ` <span class="sp-gif-wrapper relative inline-block max-w-[280px] sm:max-w-[320px] my-2 select-none" contenteditable="false"> 502 <img src="${finalImgUrl}" class="sp-comment-gif w-full rounded-xl shadow-md border border-gray-200 dark:border-gray-700 pointer-events-none" data-gurl="${finalImgUrl}" data-gid="${gif.id}"> 503 <button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full shadow-lg border-2 border-white dark:border-[#222] flex items-center justify-center font-bold text-sm leading-none hover:bg-red-600 transition-colors pointer-events-auto" title="Remover GIF">×</button> 504 </span> `; 505 document.execCommand('insertHTML', false, embedHtml); 506 closeGiphyDropdown(); 507 textarea.dispatchEvent(new Event('input')); 508 }); 509 giphyGrid.appendChild(btn); 510 }); 511 } else if (!isAppend) { 512 giphyGrid.innerHTML = `<div class="col-span-2 text-center text-xs font-bold text-gray-400 py-8">Nenhum GIF encontrado.</div>`; 513 } 514 }).catch(err => { 515 isFetchingGiphy = false; 516 if (!isAppend) giphyGrid.innerHTML = `<div class="col-span-2 text-center text-xs font-bold text-red-500 py-8">Erro do Giphy</div>`; 517 }); 518 } 519 520 // Caret Watchers 521 textarea.addEventListener('blur', () => { 522 const sel = window.getSelection(); 523 if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) { 524 container.spLastCaretRange = sel.getRangeAt(0).cloneRange(); 525 } 526 }); 527 528 textarea.addEventListener('keyup', () => { 529 const sel = window.getSelection(); 530 if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) { 531 container.spLastCaretRange = sel.getRangeAt(0).cloneRange(); 532 } 533 }); 534 535 const savedCommentKey = 'sp_forum_draft_post_' + postId; 536 const pendingDraft = localStorage.getItem(savedCommentKey); 537 // Master Form Auto Recovery (only for master form!) 538 if(container.id === 'respond' && pendingDraft && typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in === '1') { 539 textarea.innerHTML = pendingDraft; 540 localStorage.removeItem(savedCommentKey); 541 textarea.dispatchEvent(new Event('input')); 542 } 543 544 // Submit API 545 submitBtn.addEventListener('click', () => { 546 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 547 if(container.id === 'respond') localStorage.setItem(savedCommentKey, textarea.innerHTML); 548 const loginBtn = document.querySelector('.trigger-login-modal'); 549 if(loginBtn) loginBtn.click(); 550 return; 551 } 552 553 const clone = textarea.cloneNode(true); 554 clone.querySelectorAll('.sp-remove-gif, .sp-remove-yt').forEach(btn => btn.remove()); 555 clone.querySelectorAll('img.emoji, img.wp-smiley').forEach(img => { 556 const char = img.getAttribute('alt'); if(char) img.replaceWith(char); 557 }); 558 clone.querySelectorAll('span[data-userid]').forEach(span => { 559 span.replaceWith(`@[user:${span.getAttribute('data-userid')}] `); 560 }); 561 clone.querySelectorAll('img.sp-comment-upload').forEach(img => { 562 img.replaceWith(`\n\n[img]${img.getAttribute('src')}[/img]\n\n`); 563 }); 564 clone.querySelectorAll('img.sp-comment-gif').forEach(img => { 565 img.replaceWith(`\n\n[giphy]${img.getAttribute('data-gid')}[/giphy]\n\n`); 566 });
567 clone.querySelectorAll('.sp-yt-embed-preview').forEach(div => { 568 div.replaceWith(`\n\n[youtube]${div.getAttribute('data-ytid')}[/youtube]\n\n`); 569 }); 570 clone.querySelectorAll('div, p').forEach(el => { 571 el.insertAdjacentText('beforebegin', '\n'); 572 el.replaceWith(...el.childNodes); 573 }); 574 clone.querySelectorAll('br').forEach(br => br.replaceWith('\n')); 575 576 let commentTxt = clone.innerHTML.replace(/ /g, ' ').trim(); 577 if(commentTxt.replace(/<[^>]*>?/gm, '').trim().length === 0) return; 578 579 submitBtn.classList.add('hidden'); 580 spinner.classList.remove('hidden'); 581 textarea.setAttribute('contenteditable', 'false'); 582 583 const isEditingMode = editIdInput && editIdInput.value !== '0'; 584 const formData = new URLSearchParams(); 585 586 if (isEditingMode) { 587 formData.append('action', 'sp_user_edit_comment'); 588 formData.append('nonce', selepowerAjax.comment_nonce); 589 formData.append('comment_id', editIdInput.value); 590 formData.append('content', commentTxt); 591 } else { 592 formData.append('action', 'sp_submit_comment'); 593 formData.append('comment', commentTxt); 594 formData.append('comment_post_ID', postId); 595 formData.append('comment_parent', parentIdInput.value || '0'); 596 } 597 598 fetch(selepowerAjax.ajaxurl, { method: 'POST', body: formData, headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} }) 599 .then(res => { if(!res.ok) throw new Error('Erro na rede'); return res.json(); }) 600 .then(data => { 601 if(!data.success) throw new Error(data.data?.message || 'Erro'); 602 603 if (isEditingMode) { 604 const editingNode = document.querySelector(`.sp-comment-meta-holder[data-cid="${editIdInput.value}"] .sp-raw-content`); 605 if(editingNode) { 606 editingNode.innerHTML = data.data.html || commentTxt.replace(/\n\n/g, '<p></p>').replace(/\n/g, '<br/>'); 607 editingNode.classList.remove('hidden'); 608 const footerMeta = editingNode.closest('.sp-comment-meta-holder').querySelector('footer .opacity-80'); 609 if(footerMeta && !footerMeta.innerHTML.includes('(Editado)')) { 610 const edNodeSpan = document.createElement('span'); 611 edNodeSpan.className = 'text-[10px] bg-gray-200/50 dark:bg-gray-800/40 px-1.5 py-0.5 rounded shadow-sm text-gray-500 font-bold tracking-wide'; 612 edNodeSpan.innerText = '(Editado)'; 613 footerMeta.appendChild(edNodeSpan); 614 } 615 } 616 if(container.id !== 'respond') container.remove(); 617 } else { 618 const html = data.data.html; 619 const parser = new DOMParser(); 620 const vDom = parser.parseFromString(html, 'text/html'); 621 const newCommentNode = vDom.body.firstElementChild; 622 if (newCommentNode) { 623 const commentList = document.querySelector('.comment-list'); 624 if (commentList) { 625 const pid = parentIdInput.value || '0'; 626 if (pid !== "0") { 627 const parentLi = document.getElementById('comment-' + pid); 628 if (parentLi) { 629 let childrenContainer = parentLi.querySelector('.children'); 630 if (!childrenContainer) { 631 childrenContainer = document.createElement('ol'); 632 childrenContainer.className = 'children'; 633 parentLi.appendChild(childrenContainer); 634 } 635 childrenContainer.insertAdjacentElement('beforeend', newCommentNode); 636 } 637 if(container.id !== 'respond') container.remove(); // Destrói o form clonado respondendo 638 } else { 639 const emptyAviso = document.querySelector('.comment-list').nextElementSibling; 640 if(emptyAviso && emptyAviso.innerHTML.includes('Nenhum')) emptyAviso.remove(); 641 commentList.insertAdjacentElement('afterbegin', newCommentNode); 642 } 643 } 644 } 645 } 646 647 if (container.id === 'respond') { 648 // Reset master form 649 textarea.innerHTML = ''; 650 textarea.setAttribute('contenteditable', 'true'); 651 spinner.classList.add('hidden'); 652 submitBtn.classList.remove('hidden', 'bg-seleblue', 'text-white', 'pointer-events-auto'); 653 submitBtn.classList.add('bg-gray-100', 'dark:bg-darksurface', 'text-gray-500', 'opacity-50', 'curs
653or-not-allowed'); 654 } 655 }) 656 .catch(err => { 657 alert("Erro: " + err.message); 658 textarea.setAttribute('contenteditable', 'true'); 659 spinner.classList.add('hidden'); 660 submitBtn.classList.remove('hidden'); 661 }); 662 }); 663 664 // Rich text Toolbar 665 container.querySelectorAll('.sp-format-btn').forEach(btn => { 666 btn.addEventListener('click', (e) => { 667 e.preventDefault(); 668 document.execCommand(btn.getAttribute('data-cmd'), false, null); 669 textarea.focus(); 670 textarea.dispatchEvent(new Event('input')); 671 }); 672 }); 673 674 // Cancel Action (Destrói clone ou Reseta master) 675 const cancelBtn = container.querySelector('.sp-cancel-reply'); 676 if (cancelBtn) { 677 cancelBtn.addEventListener('click', () => { 678 if (editIdInput && editIdInput.value !== '0') { 679 const hk = document.querySelector(`.sp-comment-meta-holder[data-cid="${editIdInput.value}"] .sp-raw-content.hidden`); 680 if(hk) hk.classList.remove('hidden'); 681 } 682 if (container.id === 'respond') { 683 textarea.innerHTML = ''; 684 parentIdInput.value = '0'; 685 if (editIdInput) editIdInput.value = '0'; 686 const rb = container.querySelector('.sp-reply-banner'); 687 if(rb) rb.classList.add('hidden'); 688 } else { 689 container.remove(); // Fim do clone! 690 } 691 }); 692 } 693 }; 694 695 // Inicializa a Caixa Mestre do Topo da Página 696 const masterForm = document.getElementById('respond'); 697 if (masterForm) initCommentForm(masterForm); 698 699 700 // ========================================== 701 // DELEGATION MESTRE PARA EVENTOS GLOBAIS 702 // ========================================== 703 704 // Controlador Global de Cliques Fora da Ãrea Isolada do Fórum + Delete Listeners Internos 705 document.addEventListener('click', (e) => { 706 // Botão Fechar GIF ou Imagem ou Youtube (Delegation) 707 if (e.target && (e.target.classList.contains('sp-remove-gif') || e.target.classList.contains('sp-remove-yt'))) { 708 e.preventDefault(); 709 const wrapper = e.target.closest('.sp-gif-wrapper, .sp-img-wrapper, .sp-yt-embed-preview'); 710 if (wrapper) { 711 const ta = wrapper.closest('.sp-comment-textarea'); 712 wrapper.remove(); 713 if(ta) ta.dispatchEvent(new Event('input')); 714 } 715 return; 716 } 717 718 // Close Dropdowns if clicked outside their form instance 719 document.querySelectorAll('.sp-form-instance').forEach(form => { 720 if(!form.contains(e.target)) { 721 // hide all dropdowns inside this form 722 form.querySelectorAll('.sp-mentions-dropdown, .sp-emoji-dropdown, .sp-video-dropdown, .sp-giphy-dropdown').forEach(drp => { 723 drp.classList.add('hidden'); 724 }); 725 } 726 }); 727 }); 728 729 // Voto Comentário AJAX 730 document.addEventListener('click', function(e) { 731 const btn = e.target.closest('.sp-vote-btn'); 732 if (!btn) return; 733 734 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 735 alert("Faça login para votar!"); return; 736 } 737 738 const commentId = btn.getAttribute('data-id'); 739 const type = btn.getAttribute('data-type'); 740 const sc = btn.parentElement.querySelector('.sp-vote-score'); 741 const orig = sc.textContent; sc.textContent = '...'; 742 743 fetch(selepowerAjax.ajaxurl, { 744 method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 745 body: new URLSearchParams({ action: 'sp_vote_comment', comment_id: commentId, vote_type: type }) 746 }).then(r => r.json()).then(data => { 747 if(data.success) { 748 sc.textContent = data.data.score; 749 sc.className = `sp-vote-score text-[11px] min-w-[16px] text-center px-1 font-black ${data.data.score > 0 ? 'text-green-500' : (data.data.score < 0 ? 'text-red-500' : 'text-gray-500 dark:text-gray-400')}`; 750 } else sc.textContent = orig; 751 }).catch(() => { sc.textContent = orig; }); 752 }); 753 754 // Paginador Root (Ver mais comentários) 755 document.addEventListener('click', function(e) { 756 const loadBtn = e.target.closest('#sp-load-more-comments'); 757 if (!loadBtn) return; 758 e.preventDefault(); 759 760 const postid = loadBtn.getAttribute('data-postid'); 761 const page = parseInt(loadBtn.getAttribute('data-page')) || 2; 762 const sortSelect = document.getElementById('sp-comment-sort'); 763 const sortBy = sortSelect ? sortSelect.value : 'vtop'; 764 const ldr = loadBtn.querySelector('.sp-ldr'); 765 766 if (ldr) ldr.classList.remove('hidden'); 767 loadBtn.classList.add('opacity-50', 'pointer-events-none'); 768 769 fetch(selepowerAjax.ajaxurl, { 770 method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, 771 body: new URLSearchParams({ action: 'sp_load_more_comments', post_id: postid, page: page, sort_by: sortBy }) 772 }).then(r => r.json()).then(res => { 773 if (res.success && res.data.html) { 774 const list = document.querySelector('.comment-list'); 775 if (list) list.insertAdjacentHTML('beforeend', res.data.html); 776 777 if (res.data.has_more) { 778 loadBtn.setAttribute('data-page', page + 1); 779 if (ldr) ldr.classList.add('hidden'); 780 loadBtn.classList.remove('opacity-50', 'pointer-events-none'); 781 } else { 782 const wrapper = document.getElementById('sp-comments-pagination-wrapper'); 783 if (wrapper) wrapper.remove(); 784 } 785 } else { 786 if (ldr) ldr.classList.add('hidden'); 787 loadBtn.classList.remove('opacity-50', 'pointer-events-none'); 788 } 789 }); 790 }); 791 792 // Evento de Troca de Ordenador de Comentários (Select Box) 793 const sortSelect = document.getElementById('sp-comment-sort'); 794 if (sortSelect) { 795 sortSelect.addEventListener('change', function(e) { 796 const list = document.querySelector('.comment-list'); 797 const wrapper = document.getElementById('sp-comments-pagination-wrapper'); 798 const loadBtn = document.getElementById('sp-load-more-comments'); 799 800 if(list) list.style.opacity = '0.5'; 801 802 const postid = document.querySelector('.sp_post_id').value; 803 const sortBy = this.value; 804 const page = 1; 805 806 fetch(selepowerAjax.ajaxurl, { 807 method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, 808 body: new URLSearchParams({ action: 'sp_load_more_comments', post_id: postid, page: page, sort_by: sortBy }) 809 }).then(r => r.json()).then(res => { 810 if (res.success) { 811 if (list) { 812 list.innerHTML = res.data.html; 813 list.style.opacity = '1'; 814 } 815 if (res.data.has_more) { 816 if (wrapper && loadBtn) { 817 wrapper.classList.remove('hidden'); 818 loadBtn.setAttribute('data-page', '2'); 819 } 820 } else { 821 if (wrapper) wrapper.classList.add('hidden'); 822 } 823 } 824 }); 825 }); 826 } 827 828 // Paginador de Respostas (Filhos) 829 document.addEventListener('click', function(e) { 830 const btn = e.target.closest('.sp-load-more-replies'); 831 if (!btn) return; e.preventDefault(); 832 833 const pid = btn.getAttribute('data-parent'); const postid = btn.getAttribute('data-postid'); 834 const citem = btn.closest('.load-more-replies-container'); 835 const ldr = btn.querySelector('.sp-ldr'); 836 if (ldr) ldr.classList.remove('hidden'); btn.classList.add('opacity-50', 'pointer-events-none'); 837 838 fetch(selepowerAjax.ajaxurl, { 839 method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, 840 body: new URLSearchParams({ action: 'sp_load_more_replies', parent_id: pid, post_id: postid }) 841 }).then(r => r.json()).then(res => { 842 if (res.success && res.data.html) { citem.insertAdjacentHTML('beforebegin', res.data.html); citem.remove(); } 843 else { if (ldr) ldr.classList.add('hidden'); btn.classList.remove('opacity-50', 'pointer-events-none'); } 844 }); 845 }); 846 847 // Youtube FakeBox 848 const ytModal = document.getElementById('sp-forum-video-modal'); 849 const ytCloseBtn = document.getElementById('sp-forum-close-btn'); 850 const ytWrapper = document.getElementById('sp-forum-iframe-wrapper'); 851 if (ytModal && ytWrapper) { 852 document.addEventListener('click', function(e) { 853 const fb = e.target.closest('.sp-yt-fakebox'); 854 if (fb) { 855 e.preventDefault(); const vId = fb.getAttribute('data-ytid'); 856 if(!vId) return; 857 document.body.style.overflow = 'hidden'; 858 ytWrapper.innerHTML = `<iframe width="100%" height="100%" src="https://www.youtube.com/embed/${vId}?autoplay=1&rel=0&modestbranding=1" frameborder="0" allowfullscreen class="absolute inset-0 border-none"></iframe>`; 859 ytModal.classList.remove('hidden');
859 ytModal.classList.add('flex'); 860 setTimeout(() => { ytModal.classList.remove('opacity-0'); ytWrapper.classList.remove('scale-95'); ytWrapper.classList.add('scale-100'); }, 10); 861 } 862 }); 863 const closeYM = () => { 864 ytModal.classList.add('opacity-0'); ytWrapper.classList.remove('scale-100'); ytWrapper.classList.add('scale-95'); 865 setTimeout(() => { ytModal.classList.remove('flex'); ytModal.classList.add('hidden'); ytWrapper.innerHTML = ''; document.body.style.overflow = ''; }, 300); 866 }; 867 if(ytCloseBtn) ytCloseBtn.addEventListener('click', closeYM); 868 ytModal.addEventListener('click', (e) => { if(e.target === ytModal) closeYM(); }); 869 document.addEventListener('keydown', (e) => { if(e.key === 'Escape' && !ytModal.classList.contains('hidden')) closeYM(); }); 870 } 871 872 // CLONADOR RESPONDER E EDITAR (A MAGIA!) ///////////////// 873 document.addEventListener('click', function(e) { 874 875 // --- RESPONDER --- 876 const rBtn = e.target.closest('.sp-reply-trigger'); 877 if (rBtn) { 878 e.preventDefault(); 879 if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') { 880 const loginBtn = document.querySelector('.trigger-login-modal'); 881 if(loginBtn) loginBtn.click(); 882 return; 883 } 884 885 const cid = rBtn.getAttribute('data-commentid'); 886 const targetEl = document.getElementById('comment-' + cid); 887 if(!targetEl || !masterForm) return; 888 889 // Remove box anterior se houver 890 let prev = targetEl.querySelector(':scope > .sp-form-instance'); 891 if(prev) prev.remove(); 892 893 const clone = masterForm.cloneNode(true); 894 clone.id = ''; // não é master 895 896 // Layout de card 897 clone.classList.remove('pb-8', 'mb-8', 'border-b', 'border-gray-100', 'dark:border-gray-800'); 898 clone.classList.add('mt-2', 'p-3', 'md:p-4', 'bg-gray-50/50', 'dark:bg-[#1f1f1f]', 'rounded-2xl', 'shadow-sm', 'border', 'border-gray-200/60', 'dark:border-gray-800/80'); 899 900 targetEl.appendChild(clone); 901 902 clone.querySelector('.sp_comment_parent').value = cid; 903 clone.querySelector('.sp_edit_comment_id').value = '0'; 904 905 const rb = clone.querySelector('.sp-reply-banner'); 906 rb.classList.remove('hidden'); rb.classList.add('flex'); 907 clone.querySelector('.sp-reply-text').innerHTML = `<span>Respondendo a <strong>@${rBtn.getAttribute('data-author')}</strong></span>`; 908 909 const authId = rBtn.getAttribute('data-authorid') || '0'; 910 const htmlBlob = `<span class="text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded" contenteditable="false" data-userid="${authId}">${rBtn.getAttribute('data-author')}</span> `; 911 912 initCommentForm(clone); 913 914 const ta = clone.querySelector('.sp-comment-textarea'); 915 ta.innerHTML = htmlBlob; 916 ta.focus(); 917 const sel = window.getSelection(); sel.selectAllChildren(ta); sel.collapseToEnd(); 918 ta.dispatchEvent(new Event('input')); 919 setTimeout(() => { clone.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50); 920 return; 921 } 922 923 // --- EDITAR --- 924 const eBtn = e.target.closest('.sp-opt-edit'); 925 if (eBtn) { 926 e.preventDefault(); 927 const md = eBtn.closest('.sp-comment-meta-holder'); 928 const cid = md.getAttribute('data-cid'); 929 const raw = md.querySelector('.sp-raw-content'); 930 const dp = eBtn.closest('.sp-comment-dropdown'); 931 if(dp) dp.remove(); 932 933 const targetEl = document.getElementById('comment-' + cid); 934 if(!targetEl || !masterForm) return; 935 936 let prev = targetEl.querySelector(':scope > .sp-form-instance'); 937 if(prev) prev.remove(); 938 939 const clone = masterForm.cloneNode(true); 940 clone.id = ''; 941 clone.classList.remove('pb-8', 'mb-8', 'border-b', 'border-gray-100', 'dark:border-gray-800');
942 clone.classList.add('mt-2', 'p-3', 'md:p-4', 'bg-gray-50/50', 'dark:bg-[#1f1f1f]', 'rounded-2xl', 'shadow-sm', 'border', 'border-gray-200/60', 'dark:border-gray-800/80'); 943 944 targetEl.appendChild(clone); 945 if(raw) raw.classList.add('hidden'); 946 947 clone.querySelector('.sp_comment_parent').value = '0'; 948 clone.querySelector('.sp_edit_comment_id').value = cid; 949 950 const rb = clone.querySelector('.sp-reply-banner'); 951 rb.classList.remove('hidden'); rb.classList.add('flex'); 952 clone.querySelector('.sp-reply-text').innerHTML = `<span>âï¸ Editando...</span>`; 953 954 initCommentForm(clone); 955 956 const ta = clone.querySelector('.sp-comment-textarea'); 957 ta.innerHTML = raw.innerHTML.trim(); 958 959 // Rehidratar Wrappers... 960 ta.querySelectorAll('img.sp-comment-gif').forEach(img => { 961 if(!img.closest('.sp-gif-wrapper')) { 962 const finalImgUrl = img.getAttribute('src'); 963 let gid = img.getAttribute('data-gid') || (finalImgUrl.match(/media\/([\w-]+)\//) || [])[1]; 964 if(gid) { 965 const wrp = document.createElement('span'); wrp.className = 'sp-gif-wrapper relative inline-block max-w-[280px] sm:max-w-[320px] my-2 select-none'; 966 wrp.setAttribute('contenteditable','false'); img.setAttribute('data-gid', gid); 967 img.classList.add('sp-comment-gif','w-full','rounded-xl','pointer-events-none'); 968 img.parentNode.insertBefore(wrp, img); wrp.appendChild(img); 969 wrp.insertAdjacentHTML('beforeend', '<button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">×</button>'); 970 } 971 } 972 }); 973 ta.querySelectorAll('img.sp-comment-upload').forEach(img => { 974 if(!img.closest('.sp-img-wrapper')) { 975 const wrp = document.createElement('span'); wrp.className = 'sp-img-wrapper relative inline-block max-w-[300px] sm:max-w-[400px] my-2 select-none'; 976 wrp.setAttribute('contenteditable','false'); 977 img.parentNode.insertBefore(wrp, img); wrp.appendChild(img); 978 wrp.insertAdjacentHTML('beforeend', '<button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">×</button>'); 979 } 980 }); 981 ta.querySelectorAll('a.mention-link').forEach(link => { 982 const uid = link.getAttribute('href').split('/').filter(Boolean).pop(); 983 const bSpan = document.createElement('span');
984 bSpan.className = 'text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded'; 985 bSpan.setAttribute('contenteditable', 'false'); bSpan.setAttribute('data-userid', uid||'0'); 986 bSpan.innerText = link.innerText; link.replaceWith(bSpan); 987 }); 988 ta.querySelectorAll('.sp-yt-fakebox').forEach(div => { 989 const ytid = div.getAttribute('data-ytid'); 990 if(ytid) { 991 const hr = document.createElement('div'); 992 hr.innerHTML = `<div class="sp-yt-embed-preview relative max-w-[400px] aspect-video my-2 select-none bg-gray-100 dark:bg-gray-800 rounded-xl" contenteditable="false" data-ytid="${ytid}"><img src="https://i.ytimg.com/vi/${ytid}/hqdefault.jpg" class="opacity-80 pointer-events-none w-full h-full object-cover"> 993 <button class="sp-remove-yt absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">×</button></div>`; 994 div.replaceWith(hr.firstElementChild); 995 } 996 }); 997 998 ta.focus(); 999 const sel = window.getSelection(); sel.selectAllChildren(ta); sel.collapseToEnd(); 1000 ta.dispatchEvent(new Event('input')); 1001 setTimeout(() => { clone.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50); 1002 return; 1003 } 1004 1005 // DELETAR (Root Node ejetor) 1006 const dBtn = e.target.closest('.sp-opt-delete'); 1007 if (dBtn) { 1008 e.preventDefault(); 1009 if (!confirm('Tem certeza?')) return; 1010 const md = dBtn.closest('.sp-comment-meta-holder'); 1011 const cid = md.getAttribute('data-cid'); 1012 1013 // Destrói forms ativos atrelados pra trás do target root! 1014 const rootNode = document.getElementById('comment-' + cid); 1015 if(rootNode) rootNode.querySelectorAll('.sp-form-instance').forEach(f => { 1016 if(f.id !== 'respond') f.remove(); 1017 }); 1018 1019 const fs = new URLSearchParams({action: 'sp_user_delete_comment', nonce: selepowerAjax.comment_nonce, comment_id: cid }); 1020 md.style.opacity = '0.5'; 1021 fetch(selepowerAjax.ajaxurl, { method: 'POST', body: fs }).then(r=>r.json()).then(r=>{ 1022 if(r.success) { 1023 rootNode.style.transition = 'all 0.5s'; rootNode.style.opacity = '0'; rootNode.style.height = '0px'; rootNode.style.overflow = 'hidden'; 1024 setTimeout(() => rootNode.remove(), 500); 1025 } else alert(r.data.message); 1026 }); 1027 const nd = dBtn.closest('.sp-comment-dropdown'); if(nd) nd.remove(); 1028 return; 1029 } 1030 1031 // DROPDOWN (PONTINHOS) - Gerado Dinâmico (Lembrar o que o usuário me pediu na task anterior) 1032 const trigger = e.target.closest('.sp-comment-options-trigger'); 1033 if (trigger) { 1034 e.preventDefault(); 1035 const bubble = trigger.closest('.group\\/bubble') || trigger.closest('.group\/bubble'); 1036 const metaHolder = trigger.closest('.sp-comment-meta-holder'); 1037 1038 const existingDrop = bubble.querySelector('.sp-comment-dropdown'); 1039 if(existingDrop && !existingDrop.classList.contains('hidden')) { 1040 existingDrop.remove(); return; 1041 } 1042 document.querySelectorAll('.sp-comment-dropdown').forEach(d => d.remove()); 1043 const isOwn = trigger.getAttribute('data-auth') === 'own'; 1044 const dropdownHTML = ` 1045 <div class="sp-comment-dropdown absolute top-10 right-0 w-36 bg-white dark:bg-[#1a1a1a] rounded-xl shadow-xl border border-gray-100 dark:border-gray-800 py-1.5 z-[99] text-[13px] font-bold text-gray-700 transition-all duration-150 transform"> 1046 ${isOwn ? ` 1047 <button type="button" class="sp-opt-edit w-full text-left px-4 py-2 hover:bg-gray-50 flex items-center gap-2">âï¸ Editar</button> 1048 <button type="button" class="sp-opt-delete w-full text-left px-4 py-2 hover:bg-red-50 text-red-500 flex items-center gap-2">ðï¸ Excluir</button> 1049 ` : ` 1050 <button type="button" class="sp-opt-report w-full text-left px-4 py-2 hover:bg-gray-50 flex items-center gap-2">ð© Denunciar</button> 1051 `} 1052 </div> 1053 `; 1054 bubble.insertAdjacentHTML('beforeend', dropdownHTML); 1055 return; 1056 } 1057 1058 // DENUNCIAR 1059 const repBtn = e.target.closest('.sp-opt-report'); 1060 if (repBtn) { 1061 e.preventDefault(); 1062 const md = repBtn.closest('.sp-comment-meta-holder'); 1063 const rModal = document.getElementById('sp-report-modal'); 1064 if(rModal) { 1065 document.getElementById('sp-report-target-cid').value = md.getAttribute('data-cid'); 1066 rModal.classList.remove('hidden'); rModal.classList.add('flex'); 1067 } 1068 repBtn.closest('.sp-comment-dropdown').remove(); 1069 return; 1070 } 1071 }); // Fim Delegation 1072 1073}); // Fim DOMContentLoaded
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.